From c7dd1588d045322e543cac5e326bb32d94cf24ba Mon Sep 17 00:00:00 2001 From: Ao Li <5557706+aoli-al@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:56:58 -0400 Subject: [PATCH 01/21] Add feedback strategy. (#715) * Add feedback guided scheduling algorithms. * Fix tests. * Add location information to events. * fix change. --- .../CheckerCore/Actors/Events/Event.cs | 1 + .../CheckerCore/Actors/Logging/JsonWriter.cs | 30 +- .../CheckerCore/CheckerConfiguration.cs | 51 ++- Src/PChecker/CheckerCore/Pattern/EventObj.cs | 22 ++ .../Pattern/EventPatternObserver.cs | 188 +++++++++ Src/PChecker/CheckerCore/Pattern/IMatcher.cs | 10 + .../Random/IRandomValueGenerator.cs | 2 + .../SystematicTesting/ControlledRuntime.cs | 23 +- .../SystematicTesting/OperationScheduler.cs | 4 +- .../Operations/AsyncOperation.cs | 3 + .../Feedback/Coverage/AbstractSchedule.cs | 70 ++++ .../Coverage/AbstractScheduleObserver.cs | 348 +++++++++++++++++ .../Coverage/ActivityCoverageReporter.cs | 0 .../Coverage/ActorRuntimeLogEventCoverage.cs | 0 .../Coverage/ActorRuntimeLogGraphBuilder.cs | 0 .../Feedback/Coverage/ConflictOpMonitor.cs | 114 ++++++ .../Feedback}/Coverage/CoverageInfo.cs | 2 + .../Feedback/Coverage/ISendEventMonitor.cs | 15 + .../Feedback/Coverage/TimelineObserver.cs | 217 +++++++++++ .../Feedback/FeedbackGuidedStrategy.cs | 305 +++++++++++++++ .../Feedback/Generator/IGenerator.cs | 18 + .../Feedback/Generator/IInputGenerator.cs | 22 ++ .../Feedback/Generator/IScheduleGenerator.cs | 18 + .../Feedback/Generator/Mutator/IMutator.cs | 6 + .../Generator/Mutator/PCTScheduleMutator.cs | 17 + .../Generator/Mutator/POSScheduleMutator.cs | 16 + .../Generator/Mutator/PctcpScheduleMutator.cs | 19 + .../Generator/Mutator/RandomInputMutator.cs | 17 + .../Mutator/RandomScheduleMutator.cs | 17 + .../Feedback/Generator/Mutator/Utils.cs | 38 ++ .../Generator/Object/RandomChoices.cs | 50 +++ .../Generator/PCTScheduleGenerator.cs | 62 +++ .../Generator/POSScheduleGenerator.cs | 67 ++++ .../Feedback/Generator/ParametricProvider.cs | 27 ++ .../Generator/PctcpScheduleGenerator.cs | 68 ++++ .../Generator/RandomInputGenerator.cs | 101 +++++ .../Generator/RandomScheduleGenerator.cs | 59 +++ .../Feedback/IFeedbackGuidedStrategy.cs | 12 + .../Strategies/Feedback/MaxHeap.cs | 88 +++++ .../Feedback/TwoStageFeedbackStrategy.cs | 36 ++ .../Strategies/Probabilistic/PCTCP/Chain.cs | 33 ++ .../Probabilistic/PCTCP/OperationWithId.cs | 3 + .../Probabilistic/PCTCP/VectorClockWrapper.cs | 8 + .../Probabilistic/PCTCPScheduler.cs | 366 ++++++++++++++++++ .../Strategies/Probabilistic/PCTScheduler.cs | 189 +++++++++ .../Strategies/Probabilistic/POSScheduler.cs | 156 ++++++++ .../Strategies/Probabilistic/POSStrategy.cs | 48 +++ .../Probabilistic/PrioritizedScheduler.cs | 11 + .../PrioritizedSchedulingStrategy.cs | 114 ++++++ .../Probabilistic/PriorizationProvider.cs | 7 + .../PriorizationSchedulingBase.cs | 243 ++++++++++++ .../Probabilistic/QLearningStrategy.cs | 90 ++++- .../Strategies/Probabilistic/RFFScheduler.cs | 174 +++++++++ .../RandomPriorizationProvider.cs | 27 ++ .../Probabilistic/RandomStrategy.cs | 6 +- .../SystematicTesting/Strategies/Utils.cs | 44 +++ .../SystematicTesting/TestReport.cs | 13 + .../SystematicTesting/TestingEngine.cs | 213 +++++++++- .../Backend/CSharp/CSharpCodeGenerator.cs | 106 ++++- .../CompilerCore/Backend/IRTransformer.cs | 3 + Src/PCompiler/CompilerCore/Parser/PLexer.g4 | 1 + Src/PCompiler/CompilerCore/Parser/PParser.g4 | 8 + .../TypeChecker/AST/Declarations/Function.cs | 6 +- .../AST/Declarations/FunctionSignature.cs | 2 + .../AST/Statements/ConstraintStmt.cs | 15 + .../CompilerCore/TypeChecker/Analyzer.cs | 7 + .../ConstraintVariableCollector.cs | 33 ++ .../TypeChecker/ControlFlowChecker.cs | 1 + .../TypeChecker/DeclarationStubVisitor.cs | 20 + .../TypeChecker/DeclarationVisitor.cs | 24 ++ .../CompilerCore/TypeChecker/ExprVisitor.cs | 24 +- .../TypeChecker/FunctionBodyVisitor.cs | 27 ++ .../TypeChecker/FunctionValidator.cs | 2 +- .../TypeChecker/ScenarioEventVisitor.cs | 20 + .../CompilerCore/TypeChecker/Scope.cs | 8 + .../CompilerCore/TypeChecker/TypeResolver.cs | 2 +- .../TypeChecker/Types/NamedTupleEntry.cs | 11 + .../PCommandLine/Options/PCheckerOptions.cs | 64 ++- Src/PRuntimes/PCSharpRuntime/PEvent.cs | 8 +- Src/PRuntimes/PCSharpRuntime/PMachine.cs | 8 +- Tutorial/2_TwoPhaseCommit/timeline.txt | 0 81 files changed, 4223 insertions(+), 85 deletions(-) create mode 100644 Src/PChecker/CheckerCore/Pattern/EventObj.cs create mode 100644 Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs create mode 100644 Src/PChecker/CheckerCore/Pattern/IMatcher.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs rename Src/PChecker/CheckerCore/{ => SystematicTesting/Strategies/Feedback}/Coverage/ActivityCoverageReporter.cs (100%) rename Src/PChecker/CheckerCore/{ => SystematicTesting/Strategies/Feedback}/Coverage/ActorRuntimeLogEventCoverage.cs (100%) rename Src/PChecker/CheckerCore/{ => SystematicTesting/Strategies/Feedback}/Coverage/ActorRuntimeLogGraphBuilder.cs (100%) create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs rename Src/PChecker/CheckerCore/{ => SystematicTesting/Strategies/Feedback}/Coverage/CoverageInfo.cs (98%) create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/Utils.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs create mode 100644 Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs create mode 100644 Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs create mode 100644 Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs create mode 100644 Tutorial/2_TwoPhaseCommit/timeline.txt diff --git a/Src/PChecker/CheckerCore/Actors/Events/Event.cs b/Src/PChecker/CheckerCore/Actors/Events/Event.cs index a33f298172..132cc5c69b 100644 --- a/Src/PChecker/CheckerCore/Actors/Events/Event.cs +++ b/Src/PChecker/CheckerCore/Actors/Events/Event.cs @@ -11,5 +11,6 @@ namespace PChecker.Actors.Events [DataContract] public abstract class Event { + public int Loc { get; set; } } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs b/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs index 2802f9e715..acc6adc74f 100644 --- a/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs +++ b/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs @@ -11,7 +11,7 @@ namespace PChecker.Actors.Logging /// /// Class for handling generating the vector clock for a log entry /// - internal class VectorClockGenerator + public class VectorClockGenerator { /// /// Nested class for handling FIFO send receive requests. @@ -61,7 +61,7 @@ internal FifoSendReceiveMapping() /// /// Field declaration that keeps track of a global vector clock map of all the machines. /// - private readonly Dictionary> _contextVcMap; + public readonly Dictionary> ContextVcMap; /// /// Field declaration that keeps track of unprocessed send requests. I.e., when a send request happened @@ -85,7 +85,7 @@ internal FifoSendReceiveMapping() /// public VectorClockGenerator() { - _contextVcMap = new Dictionary>(); + ContextVcMap = new Dictionary>(); _unhandledSendRequests = new Dictionary>(); _machines = new HashSet(); _sendRequestsCount = new Dictionary(); @@ -205,7 +205,7 @@ private void updateMachineVcMap(string machine, Dictionary senderVc { // Get a set of all machine names to update between the sender vc map and the current machine vc map (minus the current machine) var machinesToUpdateInVc = - new HashSet(_contextVcMap[machine].Keys.Union(senderVcMap.Keys).Except(new[] { machine })); + new HashSet(ContextVcMap[machine].Keys.Union(senderVcMap.Keys).Except(new[] { machine })); // Update local machine's vector clock in _contextVcMap, outside of itself, since it was already updated (incremented) from above // right before the switch case. @@ -213,17 +213,17 @@ private void updateMachineVcMap(string machine, Dictionary senderVc // the current machine's vector clock. Details can be found here: https://en.wikipedia.org/wiki/Vector_clock foreach (var machineToUpdate in machinesToUpdateInVc) { - if (_contextVcMap[machine].TryGetValue(machineToUpdate, out var localMachineToUpdateValue)) + if (ContextVcMap[machine].TryGetValue(machineToUpdate, out var localMachineToUpdateValue)) { if (senderVcMap.TryGetValue(machineToUpdate, out var senderMachineToUpdateValue)) { - _contextVcMap[machine][machineToUpdate] = + ContextVcMap[machine][machineToUpdate] = Math.Max(senderMachineToUpdateValue, localMachineToUpdateValue); } } else { - _contextVcMap[machine].Add(machineToUpdate, senderVcMap[machineToUpdate]); + ContextVcMap[machine].Add(machineToUpdate, senderVcMap[machineToUpdate]); } } } @@ -243,11 +243,11 @@ public void HandleLogEntry(LogEntry logEntry) if (MachineIsNew(machine)) { _machines.Add(machine); - _contextVcMap.Add(machine, new Dictionary { { machine, 0 } }); + ContextVcMap.Add(machine, new Dictionary { { machine, 0 } }); } // Always update the local machine count by one on any event. - _contextVcMap[machine][machine] += 1; + ContextVcMap[machine][machine] += 1; switch (logType) { @@ -273,13 +273,13 @@ public void HandleLogEntry(LogEntry logEntry) // Update the sendReqId with the send count of it. sendReqId += $":_{_sendRequestsCount[hashedGeneralSendReqId].SentCount}"; var hashedSendReqId = HashString(sendReqId); - _unhandledSendRequests.Add(hashedSendReqId, CopyVcMap(_contextVcMap[machine])); + _unhandledSendRequests.Add(hashedSendReqId, CopyVcMap(ContextVcMap[machine])); break; // For MonitorProcessEvents, tie it to the senderMachine's current vector clock // so that there is some association in the timeline case "MonitorProcessEvent": - if (logDetails.Sender != null) updateMachineVcMap(machine, _contextVcMap[logDetails.Sender]); + if (logDetails.Sender != null) updateMachineVcMap(machine, ContextVcMap[logDetails.Sender]); break; // On dequeue OR receive event, has the string containing information about the current machine that dequeued (i.e. received the event), @@ -317,7 +317,7 @@ public void HandleLogEntry(LogEntry logEntry) } // Update the log entry with the vector clock. - logEntry.Details.Clock = CopyVcMap(_contextVcMap[machine]); + logEntry.Details.Clock = CopyVcMap(ContextVcMap[machine]); } } @@ -533,7 +533,7 @@ public class JsonWriter /// /// Vector clock generator instance to help with vector clock generation. /// - private readonly VectorClockGenerator _vcGenerator; + internal VectorClockGenerator VcGenerator {get;} /// /// Getter for accessing log entry details. @@ -552,7 +552,7 @@ public JsonWriter() { _logs = new List(); _log = new LogEntry(); - _vcGenerator = new VectorClockGenerator(); + VcGenerator = new VectorClockGenerator(); } /// @@ -716,7 +716,7 @@ public void AddToLogs(bool updateVcMap = false) { if (updateVcMap) { - _vcGenerator.HandleLogEntry(_log); + VcGenerator.HandleLogEntry(_log); } _logs.Add(_log); diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index 43e557cf92..e27f18bb4c 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -257,6 +257,12 @@ public int MaxSchedulingSteps [DataMember] public uint TestingProcessId; + /// + /// The source of the pattern generator. + /// + [DataMember] + public string PatternSource; + /// /// Additional assembly specifications to instrument for code coverage, besides those in the /// dependency graph between and the Microsoft.Coyote DLLs. @@ -286,6 +292,42 @@ public int MaxSchedulingSteps [DataMember] public string JvmArgs; + /// + /// For feedback strategy, save input if the pattern are partially matched. + /// + [DataMember] + public bool SavePartialMatch; + + /// + /// For feedback strategy, discard saved generators if the size of the buffer is greater than N. + /// + [DataMember] + public int DiscardAfter; + + /// + /// For feedback strategy, schedule generator mutations based on diversity. + /// + [DataMember] + public bool DiversityBasedPriority; + + /// + /// For feedback strategy, ignore the pattern feedback. + /// + [DataMember] + public bool IgnorePatternFeedback; + + /// + /// For feedback strategy, use priority based sampling. + /// + [DataMember] + public bool PriorityBasedSampling; + + /// + /// Enable conflict analysis for scheduling optimization. + /// + [DataMember] + public bool EnableConflictAnalysis; + /// /// Initializes a new instance of the class. /// @@ -307,7 +349,7 @@ protected CheckerConfiguration() RandomGeneratorSeed = null; IncrementalSchedulingSeed = false; PerformFullExploration = false; - MaxFairSchedulingSteps = 100000; // 10 times the unfair steps + MaxFairSchedulingSteps = 10000; // 10 times the unfair steps MaxUnfairSchedulingSteps = 10000; UserExplicitlySetMaxFairSchedulingSteps = false; TestingProcessId = 0; @@ -331,9 +373,16 @@ protected CheckerConfiguration() EnableColoredConsoleOutput = false; DisableEnvironmentExit = true; + SavePartialMatch = true; + DiscardAfter = 100; + DiversityBasedPriority = true; + IgnorePatternFeedback = false; + PriorityBasedSampling = true; + EnableConflictAnalysis = false; PSymArgs = ""; JvmArgs = ""; + PatternSource = ""; } /// diff --git a/Src/PChecker/CheckerCore/Pattern/EventObj.cs b/Src/PChecker/CheckerCore/Pattern/EventObj.cs new file mode 100644 index 0000000000..2accf8d86b --- /dev/null +++ b/Src/PChecker/CheckerCore/Pattern/EventObj.cs @@ -0,0 +1,22 @@ +using PChecker.Actors.Events; +using PChecker.Specifications.Monitors; + +namespace PChecker.Matcher; + +public class EventObj +{ + public Event Event; + public string? Sender; + public string? Receiver; + public string State; + public int Index; + + public EventObj(Event e, string? sender, string? receiver, string state, int index) + { + Event = e; + Sender = sender; + Receiver = receiver; + State = state; + Index = index; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs b/Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs new file mode 100644 index 0000000000..c0dfdaa26f --- /dev/null +++ b/Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using PChecker.Actors; +using PChecker.Actors.Events; +using PChecker.Actors.Logging; +using PChecker.Matcher; + +namespace PChecker.Feedback; + +internal class EventPatternObserver : IActorRuntimeLog +{ + private MethodInfo _matcher; + private Dictionary _senderMap = new(); + private List _events = new(); + public EventPatternObserver(MethodInfo matcher) + { + _matcher = matcher; + } + + public void OnCreateActor(ActorId id, string creatorName, string creatorType) + { + + } + + public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) + { + } + + public void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName) + { + + } + + public void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, Event e, + Guid opGroupId, bool isTargetHalted) + { + _senderMap[e] = senderName; + } + + public void OnRaiseEvent(ActorId id, string stateName, Event e) + { + + } + + public void OnEnqueueEvent(ActorId id, Event e) + { + + } + + public void OnDequeueEvent(ActorId id, string stateName, Event e) + { + _events.Add(new EventObj(e, _senderMap.GetValueOrDefault(e), id.Name, stateName, _events.Count)); + } + + + public void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked) + { + + } + + public void OnWaitEvent(ActorId id, string stateName, Type eventType) + { + + } + + public void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes) + { + + } + + public void OnStateTransition(ActorId id, string stateName, bool isEntry) + { + + } + + public void OnGotoState(ActorId id, string currentStateName, string newStateName) + { + } + + public void OnPushState(ActorId id, string currentStateName, string newStateName) + { + + } + + public void OnPopState(ActorId id, string currentStateName, string restoredStateName) + { + + } + + public void OnDefaultEventHandler(ActorId id, string stateName) + { + + } + + public void OnHalt(ActorId id, int inboxSize) + { + + } + + public void OnHandleRaisedEvent(ActorId id, string stateName, Event e) + { + + } + + public void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) + { + + } + + public void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex) + { + + } + + public void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex) + { + + } + + public void OnCreateMonitor(string monitorType) + { + + } + + public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) + { + + } + + public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, + string senderStateName, Event e) + { + _events.Add(new EventObj(e, senderName, null, stateName, _events.Count)); + } + + public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) + { + + } + + public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) + { + + } + + public void OnMonitorError(string monitorType, string stateName, bool? isInHotState) + { + + } + + public void OnRandom(object result, string callerName, string callerType) + { + + } + + public void OnAssertionFailure(string error) + { + + } + + public void OnStrategyDescription(string strategyName, string description) + { + + } + + public void OnCompleted() + { + } + + public virtual int ShouldSave() + { + return (int) _matcher.Invoke(null, new [] { _events }); + } + + public virtual bool IsMatched() + { + int result = (int) _matcher.Invoke(null, new [] { _events }); + return result == 1; + } + + + public void Reset() + { + _events.Clear(); + _senderMap.Clear(); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Pattern/IMatcher.cs b/Src/PChecker/CheckerCore/Pattern/IMatcher.cs new file mode 100644 index 0000000000..2faa030882 --- /dev/null +++ b/Src/PChecker/CheckerCore/Pattern/IMatcher.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using PChecker.Matcher; + +namespace PChecker.Feedback; + +public interface IMatcher +{ + + public int IsMatched(List events); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Random/IRandomValueGenerator.cs b/Src/PChecker/CheckerCore/Random/IRandomValueGenerator.cs index 8e3084ced9..71d58cbac3 100644 --- a/Src/PChecker/CheckerCore/Random/IRandomValueGenerator.cs +++ b/Src/PChecker/CheckerCore/Random/IRandomValueGenerator.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; + namespace PChecker.Random { /// diff --git a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs index c5983e709a..ac91696d2f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs @@ -18,6 +18,7 @@ using PChecker.Actors.Managers.Mocks; using PChecker.Coverage; using PChecker.Exceptions; +using PChecker.Feedback; using PChecker.Random; using PChecker.Runtime; using PChecker.Specifications.Monitors; @@ -65,6 +66,13 @@ internal sealed class ControlledRuntime : ActorRuntime /// internal readonly int? RootTaskId; + /// + /// The observer that extracts the timeline information of the scheduling. + /// + internal readonly TimelineObserver TimelineObserver = new(); + + public List SendEventMonitors = new(); + /// /// Returns the current hashed state of the monitors. @@ -148,6 +156,7 @@ internal ControlledRuntime(CheckerConfiguration checkerConfiguration, ISchedulin // Update the current asynchronous control flow with this runtime instance, // allowing future retrieval in the same asynchronous call stack. AssignAsyncControlFlowRuntime(this); + RegisterLog(TimelineObserver); } /// @@ -425,8 +434,7 @@ internal override void SendEvent(ActorId targetId, Event e, Actor sender, Guid o } /// - internal override async Task SendEventAndExecuteAsync(ActorId targetId, Event e, Actor sender, - Guid opGroupId) + internal override async Task SendEventAndExecuteAsync(ActorId targetId, Event e, Actor sender, Guid opGroupId) { Assert(sender is StateMachine, "Only an actor can call 'SendEventAndExecuteAsync': avoid " + "calling it directly from the test method; instead call it through a test driver actor."); @@ -460,6 +468,13 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Guid "Cannot send event '{0}' to actor id '{1}' that is not bound to an actor instance.", e.GetType().FullName, targetId.Value); + Scheduler.ScheduledOperation.LastEvent = e; + Scheduler.ScheduledOperation.LastSentReceiver = targetId.ToString(); + + foreach (var monitor in SendEventMonitors) { + monitor.OnSendEvent(sender.Id, e.Loc, targetId, LogWriter.JsonLogger.VcGenerator); + } + Scheduler.ScheduleNextEnabledOperation(AsyncOperationType.Send); ResetProgramCounter(sender as StateMachine); @@ -480,6 +495,9 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Guid return EnqueueStatus.Dropped; } + foreach (var monitor in SendEventMonitors) { + monitor.OnSendEventDone(sender.Id, e.Loc, targetId, LogWriter.JsonLogger.VcGenerator); + } var enqueueStatus = EnqueueEvent(target, e, sender, opGroupId); if (enqueueStatus == EnqueueStatus.Dropped) { @@ -517,6 +535,7 @@ private EnqueueStatus EnqueueEvent(Actor actor, Event e, Actor sender, Guid opGr LogWriter.LogSendEvent(actor.Id, sender?.Id.Name, sender?.Id.Type, stateName, e, opGroupId, isTargetHalted: false); + return actor.Enqueue(e, opGroupId, eventInfo); } diff --git a/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs index e21e6770e2..a148bd4586 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs @@ -150,7 +150,7 @@ internal void ScheduleNextEnabledOperation(AsyncOperationType type) } // Get and order the operations by their id. - var ops = OperationMap.Values.OrderBy(op => op.Id); + var ops = OperationMap.Values.OrderBy(op => op.Id).ToList(); // Try enable any operation that is currently waiting, but has its dependencies already satisfied. foreach (var op in ops) @@ -162,6 +162,8 @@ internal void ScheduleNextEnabledOperation(AsyncOperationType type) } } + // ops = Utils.FindHighPriorityOperations(ops, CheckerConfiguration.InterestingEvents); + if (!Strategy.GetNextOperation(current, ops, out var next)) { // Checks if the program has deadlocked. diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs index cac165309f..a94337e3a8 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using PChecker.Actors.Events; #if !DEBUG using System.Diagnostics; #endif @@ -49,6 +50,8 @@ internal abstract class AsyncOperation : IAsyncOperation /// True if the next awaiter is controlled, else false. /// internal bool IsAwaiterControlled; + public Event? LastEvent = null; + public string LastSentReceiver = ""; /// /// Initializes a new instance of the class. diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs new file mode 100644 index 0000000000..3d0b14d9d8 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using PChecker.Actors; +using PChecker.Random; + + +enum State { + PosReachR, + PosExctdW, + PosNoInfo, + PosSat, + NegExctdW, + NegReachW, + NegOtherW, + NegNoInfo, + NegUnsat, +} + +public record Constraint(Operation op1, Operation op2, bool positive) +{ + public override string ToString() + { + return $"({op1}, {op2}, {positive})"; + } +} +public record AbstractSchedule(HashSet constraints) { + + internal AbstractSchedule Mutate(List allConstraints, IRandomValueGenerator random) + { + List constraints = new(this.constraints); + + int op = random.Next(4); + switch (op) { + case 0: { + + int index = random.Next(allConstraints.Count); + constraints.Add(allConstraints[index]); + break; + } + case 1: { + if (constraints.Count > 1) { + int index = random.Next(constraints.Count); + constraints.RemoveAt(index); + index = random.Next(allConstraints.Count); + constraints.Add(allConstraints[index]); + } + break; + } + case 2: { + if (constraints.Count > 1) { + int index = random.Next(constraints.Count); + constraints.RemoveAt(index); + } + break; + } + case 3: { + if (constraints.Count > 1) { + int index = random.Next(constraints.Count); + var c = constraints[index]; + constraints.RemoveAt(index); + constraints.Add(new Constraint(c.op1, c.op2, !c.positive)); + } + break; + } + } + return new(constraints.ToHashSet()); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs new file mode 100644 index 0000000000..36723486f4 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs @@ -0,0 +1,348 @@ +using System.Collections.Generic; +using System.Linq; +using PChecker.Actors; +using PChecker.Actors.Logging; +using PChecker.SystematicTesting.Operations; + +internal class AbstractScheduleObserver : ISendEventMonitor +{ + AbstractSchedule abstractSchedule; + Dictionary> opQueue = new(); + Dictionary constraintState = new(); + public Dictionary> avoidSendTo = new(); + public Dictionary> avoidSchedule = new(); + public Dictionary> lookingForSchedule = new(); + public Dictionary> lookingForSendTo = new(); + public Dictionary> relevantConstraintsByReceiver = new(); + public Dictionary> relevantConstraintsByOp = new(); + public bool newConstraint = false; + + public Dictionary visitedConstraints = new(); + public Dictionary allVisitedConstraints = new(); + + public void OnNewAbstractSchedule(AbstractSchedule schedule) + { + abstractSchedule = schedule; + newConstraint = false; + + // Reset everything; + opQueue.Clear(); + avoidSendTo.Clear(); + avoidSchedule.Clear(); + constraintState.Clear(); + lookingForSchedule.Clear(); + lookingForSendTo.Clear(); + relevantConstraintsByOp.Clear(); + relevantConstraintsByReceiver.Clear(); + visitedConstraints.Clear(); + + foreach (var constraint in abstractSchedule.constraints) { + if (constraint.positive) { + constraintState[constraint] = State.PosNoInfo; + } else { + constraintState[constraint] = State.NegNoInfo; + } + + if (!avoidSendTo.ContainsKey(constraint.op1.Receiver)) { + avoidSendTo[constraint.op1.Receiver] = new(); + } + + if (!lookingForSendTo.ContainsKey(constraint.op1.Receiver)) { + lookingForSendTo[constraint.op1.Receiver] = new(); + } + + if (!relevantConstraintsByReceiver.ContainsKey(constraint.op1.Receiver)) { + relevantConstraintsByReceiver[constraint.op1.Receiver] = new(); + } + + if (!relevantConstraintsByOp.ContainsKey(constraint.op1)) { + relevantConstraintsByOp[constraint.op1] = new(); + } + + if (!relevantConstraintsByOp.ContainsKey(constraint.op2)) { + relevantConstraintsByOp[constraint.op2] = new(); + } + + if (!avoidSchedule.ContainsKey(constraint.op1)) { + avoidSchedule[constraint.op1] = new(); + } + + if (!avoidSchedule.ContainsKey(constraint.op2)) { + avoidSchedule[constraint.op2] = new(); + } + + if (!lookingForSchedule.ContainsKey(constraint.op1)) { + lookingForSchedule[constraint.op1] = new(); + } + + if (!lookingForSchedule.ContainsKey(constraint.op2)) { + lookingForSchedule[constraint.op2] = new(); + } + + + relevantConstraintsByOp[constraint.op1].Add(constraint); + relevantConstraintsByOp[constraint.op2].Add(constraint); + } + } + + + public HashSet GetRelevantConstraints(Operation op) + { + var constraints = new HashSet(); + if (relevantConstraintsByReceiver.ContainsKey(op.Receiver)) { + constraints.UnionWith(relevantConstraintsByReceiver[op.Receiver]); + } + if (relevantConstraintsByOp.ContainsKey(op)) { + constraints.UnionWith(relevantConstraintsByOp[op]); + } + return constraints; + } + + public void OnExecute(Operation op) + { + foreach (var constraint in GetRelevantConstraints(op)) + { + var newState = constraintState[constraint]; + if (constraint.positive) + { + if (constraint.op1 == op) + { + newState = State.PosExctdW; + } + else if (constraintState[constraint] == State.PosReachR && constraint.op2 == op) + { + newState = State.PosNoInfo; + } + else if (constraintState[constraint] == State.PosExctdW && constraint.op2 == op) + { + newState = State.PosSat; + relevantConstraintsByOp[constraint.op1].Remove(constraint); + relevantConstraintsByOp[constraint.op2].Remove(constraint); + } + else if (constraintState[constraint] == State.PosExctdW && avoidSendTo[op.Receiver].Contains(constraint)) + { + newState = State.PosNoInfo; + } + } + else + { + if (constraint.op1 == op) + { + newState = State.NegExctdW; + } + else if (constraintState[constraint] == State.NegExctdW && constraint.op2 != op) + { + newState = State.NegOtherW; + } + else if (constraintState[constraint] == State.NegOtherW && constraint.op2 == op) + { + newState = State.NegOtherW; + } + else if (constraintState[constraint] == State.NegExctdW && constraint.op2 == op) + { + newState = State.NegUnsat; + relevantConstraintsByOp[constraint.op1].Remove(constraint); + relevantConstraintsByOp[constraint.op2].Remove(constraint); + } + + } + if (newState != constraintState[constraint]) + { + CleanState(constraint); + constraintState[constraint] = newState; + UpdateLookFor(constraint); + } + } + + } + + public void OnNewOp(Operation op) + { + foreach (var constraint in GetRelevantConstraints(op)) + { + var newState = constraintState[constraint]; + if (constraint.positive) + { + if (constraintState[constraint] != State.PosExctdW && constraint.op2 == op) + { + newState = State.PosReachR; + } + } + else + { + if (constraint.op1 == op) + { + newState = State.NegReachW; + } + } + if (newState != constraintState[constraint]) + { + CleanState(constraint); + constraintState[constraint] = newState; + UpdateLookFor(constraint); + } + } + } + + public void CleanState(Constraint constraint) + { + switch (constraintState[constraint]) + { + case State.PosReachR: + avoidSchedule[constraint.op2].Remove(constraint); + lookingForSchedule[constraint.op1].Remove(constraint); + break; + case State.PosExctdW: + lookingForSchedule[constraint.op2].Remove(constraint); + avoidSendTo[constraint.op1.Receiver].Remove(constraint); + relevantConstraintsByReceiver[constraint.op1.Receiver].Remove(constraint); + break; + case State.NegExctdW: + avoidSchedule[constraint.op2].Remove(constraint); + lookingForSendTo[constraint.op1.Receiver].Remove(constraint); + relevantConstraintsByReceiver[constraint.op1.Receiver].Remove(constraint); + break; + case State.NegReachW: + avoidSchedule[constraint.op1].Remove(constraint); + break; + case State.NegOtherW: + lookingForSchedule[constraint.op2].Remove(constraint); + break; + } + } + + public void UpdateLookFor(Constraint constraint) + { + switch (constraintState[constraint]) + { + case State.PosReachR: + avoidSchedule[constraint.op2].Add(constraint); + lookingForSchedule[constraint.op1].Add(constraint); + break; + case State.PosExctdW: + lookingForSchedule[constraint.op2].Add(constraint); + avoidSendTo[constraint.op1.Receiver].Add(constraint); + relevantConstraintsByReceiver[constraint.op1.Receiver].Add(constraint); + break; + case State.NegExctdW: + avoidSchedule[constraint.op2].Add(constraint); + lookingForSendTo[constraint.op1.Receiver].Add(constraint); + relevantConstraintsByReceiver[constraint.op1.Receiver].Add(constraint); + break; + case State.NegReachW: + avoidSchedule[constraint.op1].Add(constraint); + break; + case State.NegOtherW: + lookingForSchedule[constraint.op2].Add(constraint); + break; + } + } + + public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) + { + var receiverName = receiver.ToString(); + var senderName = sender.ToString(); + + OnNewOp(new Operation(senderName, receiverName, loc)); + } + + public void OnSendEventDone(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) + { + var receiverName = receiver.ToString(); + var senderName = sender.ToString(); + + if (!opQueue.ContainsKey(receiverName)) + { + opQueue[receiverName] = new(); + } + var queue = opQueue[receiverName]; + + queue.Add(new Operation(senderName, receiverName, loc)); + OnExecute(new Operation(senderName, receiverName, loc)); + + if (queue.Count > 1) { + var op1 = queue[queue.Count - 2]; + var op2 = queue[queue.Count - 1]; + + var c = new Constraint(op1, op2, true); + + if (!visitedConstraints.ContainsKey(c)) { + visitedConstraints[c] = 0; + } + visitedConstraints[c] += 1; + } + } + + public int GetTraceHash() { + if (visitedConstraints.Count == 0) { + return "".GetHashCode(); + } + string s = visitedConstraints.ToList().Select(it => $"<{it.Key}, {it.Value}>").OrderBy(it => it).Aggregate((current, next) => current + "," + next); + return s.GetHashCode(); + } + + public bool CheckNoveltyAndUpdate() + { + bool isNovel = false; + foreach (var constraint in visitedConstraints) { + if (!allVisitedConstraints.ContainsKey(constraint.Key)) + { + allVisitedConstraints[constraint.Key] = 0; + } + if (constraint.Value > allVisitedConstraints[constraint.Key]) + { + allVisitedConstraints[constraint.Key] = constraint.Value; + isNovel = true; + } + } + return isNovel; + } + + public bool CheckAbstractTimelineSatisfied() + { + return constraintState.All(it => { + if (it.Key.positive) { + return it.Value == State.PosSat; + } else { + return it.Value != State.NegUnsat; + } + }); + } + + public bool ShouldAvoid(AsyncOperation op) + { + if (op.Type == AsyncOperationType.Send) + { + var operation = new Operation(op.Name, op.LastSentReceiver, op.LastEvent!.Loc); + if (avoidSchedule.ContainsKey(operation)) + { + return true; + } + if (avoidSendTo.ContainsKey(operation.Receiver) + && avoidSendTo[operation.Receiver].Any(it => it.op2 != operation)) + { + return true; + } + } + return false; + } + + public bool ShouldTake(AsyncOperation op) + { + if (op.Type == AsyncOperationType.Send) + { + var operation = new Operation(op.Name, op.LastSentReceiver, op.LastEvent!.Loc); + if (lookingForSchedule.ContainsKey(operation)) + { + return true; + } + if (lookingForSendTo.ContainsKey(operation.Receiver) && + lookingForSendTo[operation.Receiver].Any(it => it.op2 == operation)) + { + return true; + } + return false; + } + return true; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Coverage/ActivityCoverageReporter.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActivityCoverageReporter.cs similarity index 100% rename from Src/PChecker/CheckerCore/Coverage/ActivityCoverageReporter.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActivityCoverageReporter.cs diff --git a/Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogEventCoverage.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogEventCoverage.cs similarity index 100% rename from Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogEventCoverage.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogEventCoverage.cs diff --git a/Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogGraphBuilder.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogGraphBuilder.cs similarity index 100% rename from Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogGraphBuilder.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogGraphBuilder.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs new file mode 100644 index 0000000000..561d7f0e86 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using System.Linq; +using PChecker.Actors; +using PChecker.Actors.Logging; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.Feedback; + + +public class ConflictOpMonitor: ISendEventMonitor +{ + + + // This dictionary stores all operations received by a machine. + // Each operation is labeled with ActorId, source location, and its corresponding + // vector clock timestamp. + private Dictionary)>> incomingOps = new(); + + private Dictionary> conflictOps = new(); + + + public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) + { + var receiverKey = receiver.ToString(); + var senderKey = sender.ToString(); + var currentOp = new Operation(senderKey, receiverKey, loc); + if (!incomingOps.ContainsKey(receiverKey)) + { + incomingOps.Add(receiverKey, new HashSet<(Operation, Dictionary)>()); + } + var opsSet = incomingOps[receiverKey]; + + + + if (currentVc.ContextVcMap.TryGetValue(sender.Name, out var vectorClock)) + { + + foreach (var op in opsSet) + { + if (op.Item1.Sender == currentOp.Sender) + { + continue; + } + + if (!IsLEQ(op.Item2, vectorClock) && !IsLEQ(vectorClock, op.Item2)) + { + AddConflictOp(op.Item1, currentOp); + } + } + opsSet.Add((currentOp, vectorClock + .ToDictionary(entry => entry.Key, entry => entry.Value))); + } + } + + public void OnSendEventDone(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) {} + + internal bool IsRacing(AsyncOperation op1, AsyncOperation op2) + { + if (op1.Type != AsyncOperationType.Send || op2.Type != AsyncOperationType.Send) { + return false; + } + + var operation1 = new Operation(op1.Name, op1.LastSentReceiver, op1.LastEvent!.Loc); + var operation2 = new Operation(op2.Name, op2.LastSentReceiver, op2.LastEvent!.Loc); + + if (conflictOps.TryGetValue(operation1, out var ops)) { + return ops.Contains(operation2); + } + return false; + } + + public void Reset() { + incomingOps.Clear(); + } + + bool IsLEQ(Dictionary vc1, Dictionary vc2) + { + foreach (var key in vc1.Keys.Union(vc2.Keys)) + { + var op1 = vc1.GetValueOrDefault(key, 0); + var op2 = vc2.GetValueOrDefault(key, 0); + if (op1 > op2) + { + return false; + } + } + return true; + } + + void AddConflictOp(Operation op1, Operation op2) + { + if (!conflictOps.ContainsKey(op1)) { + conflictOps[op1] = new HashSet(); + } + if (!conflictOps.ContainsKey(op2)) { + conflictOps[op2] = new HashSet(); + } + conflictOps[op1].Add(op2); + conflictOps[op2].Add(op1); + } + internal bool IsConflictingOp(AsyncOperation op) + { + if (op.Type != AsyncOperationType.Send) { + return false; + } + + var operation = new Operation(op.Name, op.LastSentReceiver, op.LastEvent!.Loc); + if (conflictOps.TryGetValue(operation, out var ops )) + { + return ops.Count != 0; + } + return false; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Coverage/CoverageInfo.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/CoverageInfo.cs similarity index 98% rename from Src/PChecker/CheckerCore/Coverage/CoverageInfo.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/CoverageInfo.cs index dcca7a6f00..642e88e561 100644 --- a/Src/PChecker/CheckerCore/Coverage/CoverageInfo.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/CoverageInfo.cs @@ -54,6 +54,8 @@ public CoverageInfo() Machines = new HashSet(); MachinesToStates = new Dictionary>(); RegisteredEvents = new Dictionary>(); + EventInfo = new EventCoverage(); + CoverageGraph = new Graph(); } /// diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs new file mode 100644 index 0000000000..394ddb3e9e --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs @@ -0,0 +1,15 @@ +using PChecker.Actors; +using PChecker.Actors.Logging; + +public record Operation(string Sender, string Receiver, int Loc) { + public override string ToString() + { + return $"<{Sender}, {Receiver}, {Loc}>"; + } + +} +public interface ISendEventMonitor { + public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc); + + public void OnSendEventDone(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs new file mode 100644 index 0000000000..0c0759e3ca --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.Actors; +using PChecker.Actors.Events; +using PChecker.Actors.Logging; + +namespace PChecker.Feedback; + +public class TimelineObserver: IActorRuntimeLog +{ + + private HashSet<(string, string, string)> _timelines = new(); + private Dictionary> _allEvents = new(); + private Dictionary> _orderedEvents = new(); + + public static readonly List<(int, int)> Coefficients = new(); + public static int NumOfCoefficients = 50; + + static TimelineObserver() + { + // Fix seed to generate same random numbers across runs. + var rand = new System.Random(0); + + for (int i = 0; i < NumOfCoefficients; i++) + { + Coefficients.Add((rand.Next(), rand.Next())); + } + } + + public void OnCreateActor(ActorId id, string creatorName, string creatorType) + { + + } + + public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) + { + } + + public void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName) + { + + } + + public void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, Event e, + Guid opGroupId, bool isTargetHalted) + { + } + + public void OnRaiseEvent(ActorId id, string stateName, Event e) + { + + } + + public void OnEnqueueEvent(ActorId id, Event e) + { + + } + + public void OnDequeueEvent(ActorId id, string stateName, Event e) + { + string actor = id.Type; + + _allEvents.TryAdd(actor, new()); + _orderedEvents.TryAdd(actor, new()); + + string name = e.GetType().Name; + foreach (var ev in _allEvents[actor]) + { + _timelines.Add((actor, ev, name)); + } + _allEvents[actor].Add(name); + _orderedEvents[actor].Add(name); + } + + public int GetTimelineHash() + { + return GetAbstractTimeline().GetHashCode(); + } + + public string GetAbstractTimeline() + { + var tls = _timelines.Select(it => $"<{it.Item1}, {it.Item2}, {it.Item3}>").ToList(); + tls.Sort(); + return string.Join(";", tls); + } + + public string GetTimeline() + { + return string.Join(";", _orderedEvents.Select(it => + { + var events = string.Join(",", it.Value); + return $"{it.Key}: {events}"; + })); + } + + public List GetTimelineMinhash() + { + List minHash = new(); + var timelineHash = _timelines.Select(it => it.GetHashCode()); + foreach (var (a, b) in Coefficients) + { + int minValue = Int32.MaxValue; + foreach (var value in timelineHash) + { + int hash = a * value + b; + minValue = Math.Min(minValue, hash); + } + minHash.Add(minValue); + } + return minHash; + } + + public void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked) + { + + } + + public void OnWaitEvent(ActorId id, string stateName, Type eventType) + { + + } + + public void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes) + { + + } + + public void OnStateTransition(ActorId id, string stateName, bool isEntry) + { + + } + + public void OnGotoState(ActorId id, string currentStateName, string newStateName) + { + } + + public void OnDefaultEventHandler(ActorId id, string stateName) + { + + } + + public void OnHalt(ActorId id, int inboxSize) + { + + } + + public void OnHandleRaisedEvent(ActorId id, string stateName, Event e) + { + + } + + public void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) + { + + } + + public void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex) + { + + } + + public void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex) + { + + } + + public void OnCreateMonitor(string monitorType) + { + + } + + public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) + { + + } + + public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, + string senderStateName, Event e) + { + + } + + public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) + { + + } + + public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) + { + + } + + public void OnMonitorError(string monitorType, string stateName, bool? isInHotState) + { + + } + + public void OnRandom(object result, string callerName, string callerType) + { + + } + + public void OnAssertionFailure(string error) + { + + } + + public void OnStrategyDescription(string strategyName, string description) + { + + } + + public void OnCompleted() + { + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs new file mode 100644 index 0000000000..7d3f4a82f7 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using PChecker.Generator; +using PChecker.Feedback; +using AsyncOperation = PChecker.SystematicTesting.Operations.AsyncOperation; +using Debug = System.Diagnostics.Debug; + +namespace PChecker.SystematicTesting.Strategies.Feedback; + + +internal class FeedbackGuidedStrategy : IFeedbackGuidedStrategy + where TInput: IInputGenerator + where TSchedule: IScheduleGenerator +{ + public record StrategyGenerator(TInput InputGenerator, TSchedule ScheduleGenerator); + + public record GeneratorRecord(int Priority, StrategyGenerator Generator, List MinHash); + + protected StrategyGenerator Generator; + + private readonly int _maxScheduledSteps; + + protected int ScheduledSteps; + + private readonly HashSet _visitedTimelines = new(); + + private List _savedGenerators = new (); + private int _pendingMutations = 0; + private HashSet _visitedGenerators = new HashSet(); + private GeneratorRecord? _currentParent = null; + + private readonly bool _savePartialMatch; + private readonly bool _diversityBasedPriority; + private readonly bool _ignorePatternFeedback; + private readonly int _discardAfter; + private readonly bool _priorityBasedSampling; + private System.Random _rnd = new System.Random(); + + + + /// + /// Initializes a new instance of the class. + /// + public FeedbackGuidedStrategy(CheckerConfiguration checkerConfiguration, TInput input, TSchedule schedule) + { + if (schedule is PctScheduleGenerator) + { + _maxScheduledSteps = checkerConfiguration.MaxUnfairSchedulingSteps; + } + else + { + _maxScheduledSteps = checkerConfiguration.MaxFairSchedulingSteps; + } + Generator = new StrategyGenerator(input, schedule); + _savePartialMatch = checkerConfiguration.SavePartialMatch; + _diversityBasedPriority = checkerConfiguration.DiversityBasedPriority; + _discardAfter = checkerConfiguration.DiscardAfter; + _ignorePatternFeedback = checkerConfiguration.IgnorePatternFeedback; + _priorityBasedSampling = checkerConfiguration.PriorityBasedSampling; + } + + /// + public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + // var enabledOperations = _nfa != null? _nfa.FindHighPriorityOperations(ops) : ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + next = Generator.ScheduleGenerator.NextRandomOperation(ops.ToList(), current); + ScheduledSteps++; + return next != null; + } + + /// + public bool GetNextBooleanChoice(AsyncOperation current, int maxValue, out bool next) + { + next = Generator.InputGenerator.Next(maxValue) == 0; + ScheduledSteps++; + return true; + } + + /// + public bool GetNextIntegerChoice(AsyncOperation current, int maxValue, out int next) + { + next = Generator.InputGenerator.Next(maxValue); + ScheduledSteps++; + return true; + } + + /// + public virtual bool PrepareForNextIteration() + { + ScheduledSteps = 0; + PrepareNextInput(); + return true; + } + + /// + public int GetScheduledSteps() + { + return ScheduledSteps; + } + + /// + public bool HasReachedMaxSchedulingSteps() + { + if (_maxScheduledSteps == 0) + { + return false; + } + + return ScheduledSteps >= _maxScheduledSteps; + } + + /// + public bool IsFair() + { + return true; + } + + /// + public string GetDescription() + { + return "feedback"; + } + + /// + public void Reset() + { + ScheduledSteps = 0; + } + + private int ComputeDiversity(int timeline, List hash) + { + if (!_visitedTimelines.Add(timeline)) + { + return 0; + } + + if (!_priorityBasedSampling) + { + return 20; + } + + if (_savedGenerators.Count == 0 || !_diversityBasedPriority) + { + return 20; + } + + var maxSim = int.MinValue; + foreach (var record in _savedGenerators) + { + var timelineHash = record.MinHash; + var similarity = 0; + for (int i = 0; i < hash.Count; i++) + { + if (hash[i] == timelineHash[i]) + { + similarity += 1; + } + } + + maxSim = Math.Max(maxSim, similarity); + } + + + return (hash.Count - maxSim) * 10 + 20; + } + + /// + /// This method observes the results of previous run and prepare for the next run. + /// + /// The ControlledRuntime of previous run. + public virtual void ObserveRunningResults(EventPatternObserver patternObserver, ControlledRuntime runtime) + { + var timelineHash = runtime.TimelineObserver.GetTimelineHash(); + var timelineMinhash = runtime.TimelineObserver.GetTimelineMinhash(); + + int diversityScore = ComputeDiversity(timelineHash, timelineMinhash); + + if (diversityScore == 0) + { + return; + } + + int priority = 0; + if (patternObserver == null || _ignorePatternFeedback || !_priorityBasedSampling) + { + priority = diversityScore; + } + else + { + int coverageResult = patternObserver.ShouldSave(); + if (coverageResult == 1 || _savePartialMatch) + { + double coverageScore = 1.0 / coverageResult; + priority = (int)(diversityScore * coverageScore); + } + } + + if (priority > 0) + { + var record = new GeneratorRecord(priority, Generator, timelineMinhash); + if (_savedGenerators.Count == 0) + { + _savedGenerators.Add(record); + return; + } + + // Maybe use binary search to speed up in the future. + var index = 0; + while (index < _savedGenerators.Count && priority < _savedGenerators[index].Priority) + { + index += 1; + } + if (index >= _savedGenerators.Count) + { + _savedGenerators.Add(record); + } + else + { + _savedGenerators.Insert(index, record); + } + + if (_priorityBasedSampling && _savedGenerators.Count > _discardAfter) + { + var last = _savedGenerators.Last(); + _visitedGenerators.Remove(last); + _savedGenerators.RemoveAt(_savedGenerators.Count - 1); + } + } + } + + public int TotalSavedInputs() + { + return _savedGenerators.Count; + } + + private void PrepareNextInput() + { + Generator.ScheduleGenerator.PrepareForNextInput(); + if (_savedGenerators.Count == 0) + { + // Mutate current input if no input is saved. + Generator = NewGenerator(); + } + else + { + if (!_priorityBasedSampling && _pendingMutations == 0) + { + _currentParent = _savedGenerators[_rnd.Next(_savedGenerators.Count)]; + _pendingMutations = 50; + } + + if (_currentParent == null) + { + _currentParent = _savedGenerators.First(); + _visitedGenerators.Add(_currentParent); + _pendingMutations = _currentParent.Priority; + _pendingMutations = 50; + } + + if (_pendingMutations == 0) + { + bool found = false; + foreach (var generator in _savedGenerators) + { + if (_visitedGenerators.Contains(generator)) continue; + _currentParent = generator; + _visitedGenerators.Add(generator); + _pendingMutations = generator.Priority; + _pendingMutations = 50; + found = true; + } + + if (!found) + { + _visitedGenerators.Clear(); + _currentParent = _savedGenerators.First(); + _visitedGenerators.Add(_currentParent); + _pendingMutations = _currentParent.Priority; + _pendingMutations = 50; + } + } + + Generator = MutateGenerator(_currentParent.Generator); + _pendingMutations -= 1; + } + } + + + protected virtual StrategyGenerator MutateGenerator(StrategyGenerator prev) + { + return new StrategyGenerator(prev.InputGenerator.Mutate(), prev.ScheduleGenerator.Mutate()); + } + + protected virtual StrategyGenerator NewGenerator() + { + return new StrategyGenerator(Generator.InputGenerator.New(), Generator.ScheduleGenerator.New()); + } + + public void DumpStats(TextWriter writer) + { + writer.WriteLine($"..... Total saved: {TotalSavedInputs()}, pending mutations: {_pendingMutations}, visited generators: {_visitedGenerators.Count}"); + } +} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs new file mode 100644 index 0000000000..6a043dc691 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs @@ -0,0 +1,18 @@ +namespace PChecker.Generator; + +public interface IGenerator +{ + /// + /// Mutate the current generator and create a new one. + /// + /// A new generator. + T Mutate(); + + /// + /// Copy the current generator and create a new one. + /// + /// A new generator. + T Copy(); + + T New(); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs new file mode 100644 index 0000000000..a8ce3b097f --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs @@ -0,0 +1,22 @@ +namespace PChecker.Generator; + +public interface IInputGenerator : IGenerator +{ + + /// + /// Returns a non-negative random number. + /// + int Next(); + + /// + /// Returns a non-negative random number less than maxValue. + /// + /// Exclusive upper bound + int Next(int maxValue); + + /// + /// Returns a random floating-point number that is greater + /// than or equal to 0.0, and less than 1.0. + /// + double NextDouble(); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs new file mode 100644 index 0000000000..4850f55ef6 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.Generator; + +internal interface IScheduleGenerator: IGenerator +{ + /// + /// Get the next scheduled operation. + /// + /// All enabled operations. + /// Current operation. + /// Next enabled operation. + public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current); + + + public void PrepareForNextInput(); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs new file mode 100644 index 0000000000..93302c6d3a --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs @@ -0,0 +1,6 @@ +namespace PChecker.Generator.Mutator; + +public interface IMutator +{ + T Mutate(T prev); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs new file mode 100644 index 0000000000..eec20e4076 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs @@ -0,0 +1,17 @@ +namespace PChecker.Generator.Mutator; + +internal class PCTScheduleMutator : IMutator +{ + private int _meanMutationCount = 5; + private int _meanMutationSize = 5; + private System.Random _random = new(); + public PctScheduleGenerator Mutate(PctScheduleGenerator prev) + { + return new PctScheduleGenerator(prev.Random, + Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), + Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random), + prev.MaxPrioritySwitchPoints, + prev.ScheduleLength + ); + } +} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs new file mode 100644 index 0000000000..d94b042661 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs @@ -0,0 +1,16 @@ +namespace PChecker.Generator.Mutator; + +internal class POSScheduleMutator: IMutator +{ + private int _meanMutationCount = 5; + private int _meanMutationSize = 5; + private System.Random _random = new(); + public POSScheduleGenerator Mutate(POSScheduleGenerator prev) + { + return new POSScheduleGenerator(prev.Random, + Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), + Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random), + prev.Monitor + ); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs new file mode 100644 index 0000000000..b057aeac5a --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs @@ -0,0 +1,19 @@ +namespace PChecker.Generator.Mutator; + +internal class PctcpScheduleMutator: IMutator +{ + private int _meanMutationCount = 5; + private int _meanMutationSize = 5; + private System.Random _random = new(); + public PctcpScheduleGenerator Mutate(PctcpScheduleGenerator prev) + { + return new PctcpScheduleGenerator(prev.Random, + Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), + Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random), + prev.MaxPrioritySwitchPoints, + prev.ScheduleLength, + prev.VcWrapper + ); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs new file mode 100644 index 0000000000..33409f939a --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; +using PChecker.Generator.Object; + +namespace PChecker.Generator.Mutator; + +public class RandomInputMutator : IMutator +{ + private readonly int _meanMutationCount = 10; + private readonly int _meanMutationSize = 10; + private System.Random _random = new(); + public RandomInputGenerator Mutate(RandomInputGenerator prev) + { + return new RandomInputGenerator(prev.Random, Utils.MutateRandomChoices(prev.IntChoices, _meanMutationCount, _meanMutationSize, _random), + Utils.MutateRandomChoices(prev.DoubleChoices, _meanMutationCount, _meanMutationSize, _random)); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs new file mode 100644 index 0000000000..ac6aed409c --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using PChecker.Generator.Object; + +namespace PChecker.Generator.Mutator; + +internal class RandomScheduleMutator : IMutator +{ + private readonly int _meanMutationCount = 10; + private readonly int _meanMutationSize = 10; + private System.Random _random = new(); + public RandomScheduleGenerator Mutate(RandomScheduleGenerator prev) + { + return new RandomScheduleGenerator(prev.Random, Utils.MutateRandomChoices(prev.IntChoices, _meanMutationCount, _meanMutationSize, _random)); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/Utils.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/Utils.cs new file mode 100644 index 0000000000..8ae1e5b165 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/Utils.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq; +using PChecker.Generator.Object; + +namespace PChecker.Generator.Mutator; + +public class Utils +{ + public static int SampleGeometric(double p, double random) { + var result = Math.Ceiling(Math.Log(1 - random) / Math.Log(1 - p)); + return (int)result; + } + public static RandomChoices MutateRandomChoices (RandomChoices randomChoices, int meanMutationCount, int meanMutationSize, System.Random random) + where T: IConvertible + { + meanMutationCount = Math.Max(Math.Min(randomChoices.Data.Count / 3, meanMutationCount), 1); + meanMutationSize = Math.Max(Math.Min(randomChoices.Data.Count / 3, meanMutationSize), 1); + RandomChoices newChoices = new RandomChoices(randomChoices); + int mutations = Utils.SampleGeometric(1.0f / meanMutationCount, random.NextDouble()); + + while (mutations-- > 0) + { + int offset = random.Next(newChoices.Data.Count); + int mutationSize = Utils.SampleGeometric(1.0f / meanMutationSize, random.NextDouble()); + for (int i = offset; i < offset + mutationSize; i++) + { + if (i >= newChoices.Data.Count) + { + break; + } + + newChoices.Data[i] = newChoices.GenerateNew(); + } + } + + return newChoices; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs new file mode 100644 index 0000000000..e32d1e007e --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using PChecker.Exceptions; + +namespace PChecker.Generator.Object; + +public class RandomChoices +where T: IConvertible +{ + private readonly System.Random _random; + public int Pos; + public List Data = new(); + + public RandomChoices(System.Random random) + { + _random = random; + } + + public RandomChoices(RandomChoices other) + { + _random = other._random; + Data = new List(other.Data); + } + + public T Next() + { + if (Pos == Data.Count) + { + Data.Add(GenerateNew()); + } + return Data[Pos++]; + } + + public T GenerateNew() + { + if (typeof(T).IsAssignableFrom(typeof(int))) + { + return (T) Convert.ChangeType(_random.Next(), typeof(T)); + } + else if (typeof(T).IsAssignableFrom(typeof(double))) + { + return (T) Convert.ChangeType(_random.NextDouble(), typeof(T)); + } + else + { + throw new RuntimeException("The random choices only supports int and double type."); + } + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs new file mode 100644 index 0000000000..0a0ed19401 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using PChecker.Generator.Mutator; +using PChecker.Generator.Object; +using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Probabilistic; + +namespace PChecker.Generator; + +internal sealed class PctScheduleGenerator: PCTScheduler, IScheduleGenerator +{ + public System.Random Random; + public RandomChoices PriorityChoices; + public RandomChoices SwitchPointChoices; + + public PctScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? switchPointChoices, int numSwitchPoints, int maxScheduleLength): + base(numSwitchPoints, maxScheduleLength, + new ParametricProvider( + priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), + switchPointChoices != null ? new RandomChoices(switchPointChoices) : new RandomChoices(random))) + { + Random = random; + var provider = (ParametricProvider) Provider; + PriorityChoices = provider.PriorityChoices; + SwitchPointChoices = provider.SwitchPointChoices; + } + + public PctScheduleGenerator(CheckerConfiguration checkerConfiguration): + this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, null, checkerConfiguration.StrategyBound, 0) + { + } + + public PctScheduleGenerator Mutate() + { + return new PCTScheduleMutator().Mutate(this); + } + + public PctScheduleGenerator New() + { + return new PctScheduleGenerator(Random, null, null, MaxPrioritySwitchPoints, ScheduleLength); + } + + public PctScheduleGenerator Copy() + { + return new PctScheduleGenerator(Random, PriorityChoices, SwitchPointChoices, MaxPrioritySwitchPoints, ScheduleLength); + } + + public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) + { + if (GetNextOperation(current, enabledOperations, out var next)) { + return next; + } else { + return null; + } + } + + + public void PrepareForNextInput() + { + PrepareForNextIteration(); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs new file mode 100644 index 0000000000..fffc0ba15d --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using PChecker.Feedback; +using PChecker.Generator.Mutator; +using PChecker.Generator.Object; +using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Probabilistic; + +namespace PChecker.Generator; + +internal class POSScheduleGenerator: POSScheduler, IScheduleGenerator +{ + public System.Random Random; + public RandomChoices PriorityChoices; + public RandomChoices SwitchPointChoices; + public ConflictOpMonitor? Monitor; + + public POSScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? switchPointChoices, + ConflictOpMonitor? monitor): + base(new ParametricProvider( + priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), + switchPointChoices != null ? new RandomChoices(switchPointChoices) : new RandomChoices(random)), + monitor) + { + Random = random; + var provider = (ParametricProvider) Provider; + PriorityChoices = provider.PriorityChoices; + SwitchPointChoices = provider.SwitchPointChoices; + Monitor = monitor; + } + + public POSScheduleGenerator(CheckerConfiguration checkerConfiguration, ConflictOpMonitor? monitor): + this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, + null, monitor) + { + } + + public POSScheduleGenerator Mutate() + { + return new POSScheduleMutator().Mutate(this); + } + + public POSScheduleGenerator New() + { + return new POSScheduleGenerator(Random, null, null, Monitor); + } + + public POSScheduleGenerator Copy() + { + return new POSScheduleGenerator(Random, PriorityChoices, SwitchPointChoices, Monitor); + } + + public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) + { + if (GetNextOperation(current, enabledOperations, out var next)) { + return next; + } else { + return null; + } + } + + public void PrepareForNextInput() + { + PrepareForNextIteration(); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs new file mode 100644 index 0000000000..0ffc657564 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs @@ -0,0 +1,27 @@ +using PChecker.Generator.Object; +using PChecker.SystematicTesting.Strategies.Probabilistic; + +namespace PChecker.Generator; + +internal class ParametricProvider: PriorizationProvider +{ + public RandomChoices PriorityChoices; + public RandomChoices SwitchPointChoices; + public ParametricProvider(RandomChoices priority, RandomChoices switchPoint) + { + PriorityChoices = priority; + SwitchPointChoices = switchPoint; + } + + public int AssignPriority(int numOps) + { + + return PriorityChoices.Next() % numOps + 1; + } + + public double SwitchPointChoice() + { + return SwitchPointChoices.Next(); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs new file mode 100644 index 0000000000..34ffe087d0 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using PChecker.Generator.Mutator; +using PChecker.Generator.Object; +using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Probabilistic; +using PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; + +namespace PChecker.Generator; + +internal class PctcpScheduleGenerator: PCTCPScheduler, IScheduleGenerator +{ + + public System.Random Random; + public RandomChoices PriorityChoices; + public RandomChoices SwitchPointChoices; + + public PctcpScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? + switchPointChoices, int numSwitchPoints, int maxScheduleLength, VectorClockWrapper wrapper): + base(numSwitchPoints, maxScheduleLength, + new ParametricProvider( + priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), + switchPointChoices != null ? new RandomChoices(switchPointChoices) : new + RandomChoices(random)), wrapper) + { + Random = random; + var provider = (ParametricProvider) Provider; + PriorityChoices = provider.PriorityChoices; + SwitchPointChoices = provider.SwitchPointChoices; + } + public PctcpScheduleGenerator(CheckerConfiguration checkerConfiguration, VectorClockWrapper wrapper): + this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, + null, checkerConfiguration.StrategyBound, 0, wrapper) + { + } + + public PctcpScheduleGenerator Mutate() + { + return new PctcpScheduleMutator().Mutate(this); + } + + public PctcpScheduleGenerator New() + { + return new PctcpScheduleGenerator(Random, null, null, MaxPrioritySwitchPoints, ScheduleLength, VcWrapper); + } + + public PctcpScheduleGenerator Copy() + { + return new PctcpScheduleGenerator(Random, PriorityChoices, SwitchPointChoices, MaxPrioritySwitchPoints, + ScheduleLength, VcWrapper); + } + + public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) + { + if (GetNextOperation(current, enabledOperations, out var next)) { + return next; + } else { + return null; + } + } + + + public void PrepareForNextInput() + { + PrepareForNextIteration(); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs new file mode 100644 index 0000000000..bde03ddd3c --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs @@ -0,0 +1,101 @@ +using System; +using System.Diagnostics; +using System.IO; +using PChecker.Generator.Mutator; +using PChecker.Generator.Object; + +namespace PChecker.Generator +{ + + /// + /// This class implements a JQF-style stream-based input generator. + /// See more: https://github.com/rohanpadhye/JQF + /// + public class RandomInputGenerator : IInputGenerator + { + /// + /// Device for generating random numbers. + /// + internal readonly System.Random Random; + + internal RandomChoices IntChoices; + internal RandomChoices DoubleChoices; + + public RandomInputGenerator(System.Random random, RandomChoices? intChoices, RandomChoices? doubleChoices) + { + Random = random; + IntChoices = intChoices != null ? new RandomChoices(intChoices) : new RandomChoices(Random); + DoubleChoices = doubleChoices != null ? new RandomChoices(doubleChoices) : new RandomChoices(Random); + } + + + /// + /// Create a stream based value generator using CheckerConfiguration. + /// + /// + public RandomInputGenerator(CheckerConfiguration checkerConfiguration): + this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, null) + { + } + + /// + /// Create a default stream based value generator. + /// + public RandomInputGenerator(): + this(new System.Random(Guid.NewGuid().GetHashCode()), null, null) + { + } + + + /// + /// Create a stream based value generator with an existing generator. + /// + /// + public RandomInputGenerator(RandomInputGenerator other) : this(other.Random, other.IntChoices, other.DoubleChoices) + { + } + + /// + /// Returns a non-negative random number. + /// + public int Next() + { + return IntChoices.Next(); + } + + + /// + /// Returns a non-negative random number less than the specified max value. + /// + /// Exclusive upper bound. + public int Next(int maxValue) + { + var value = maxValue == 0 ? 0 : Next() % maxValue; + return value; + } + + /// + /// Returns a random floating-point number that is greater + /// than or equal to 0.0, and less than 1.0. + /// + public double NextDouble() + { + return DoubleChoices.Next(); + } + + public RandomInputGenerator Mutate() + { + return new RandomInputMutator().Mutate(this); + } + + public RandomInputGenerator Copy() + { + return new RandomInputGenerator(this); + } + + public RandomInputGenerator New() + { + return new RandomInputGenerator(Random, null, null); + } + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs new file mode 100644 index 0000000000..98f64b77a8 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using PChecker.Generator.Mutator; +using PChecker.Generator.Object; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.Generator; + +internal class RandomScheduleGenerator: IScheduleGenerator +{ + internal readonly System.Random Random; + + public RandomChoices IntChoices; + + public RandomScheduleGenerator(System.Random random, RandomChoices? intChoices) + { + Random = random; + IntChoices = intChoices != null ? new RandomChoices(intChoices) : new RandomChoices(Random); + } + + public RandomScheduleGenerator(CheckerConfiguration checkerConfiguration): + this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()) , null) + { + } + + public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) + { + if (enabledOperations.Count == 0) + { + return null; + } + + if (enabledOperations.Count == 1) + { + return enabledOperations[0]; + } + var idx = IntChoices.Next() % enabledOperations.Count; + return enabledOperations[idx]; + } + + public RandomScheduleGenerator Mutate() + { + return new RandomScheduleMutator().Mutate(this); + } + + public RandomScheduleGenerator Copy() + { + return new RandomScheduleGenerator(Random, IntChoices); + } + + public void PrepareForNextInput() + { + } + + public RandomScheduleGenerator New() + { + return new RandomScheduleGenerator(Random, null); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs new file mode 100644 index 0000000000..84f3eec289 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs @@ -0,0 +1,12 @@ +using System; +using System.IO; +using PChecker.Feedback; + +namespace PChecker.SystematicTesting.Strategies.Feedback; + +internal interface IFeedbackGuidedStrategy: ISchedulingStrategy +{ + public void ObserveRunningResults(EventPatternObserver patternObserver, ControlledRuntime runtime); + public int TotalSavedInputs(); + public void DumpStats(TextWriter writer); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs new file mode 100644 index 0000000000..b04f504de4 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; + +namespace PChecker.SystematicTesting.Strategies.Feedback; + +public class MaxHeap +{ + public readonly List Elements = new(); + private IComparer _comparer; + + public MaxHeap(IComparer cmp) + { + _comparer = cmp; + } + + private int GetLeftChildIndex(int elementIndex) => 2 * elementIndex + 1; + private int GetRightChildIndex(int elementIndex) => 2 * elementIndex + 2; + private int GetParentIndex(int elementIndex) => (elementIndex - 1) / 2; + + private bool HasLeftChild(int elementIndex) => GetLeftChildIndex(elementIndex) < Elements.Count; + private bool HasRightChild(int elementIndex) => GetRightChildIndex(elementIndex) < Elements.Count; + private bool IsRoot(int elementIndex) => elementIndex == 0; + + private TValue GetLeftChild(int elementIndex) => Elements[GetLeftChildIndex(elementIndex)]; + private TValue GetRightChild(int elementIndex) => Elements[GetRightChildIndex(elementIndex)]; + private TValue GetParent(int elementIndex) => Elements[GetParentIndex(elementIndex)]; + + private void Swap(int firstIndex, int secondIndex) + { + (Elements[firstIndex], Elements[secondIndex]) = (Elements[secondIndex], Elements[firstIndex]); + } + + public TValue Peek() + { + return Elements[0]; + } + + public TValue Pop() + { + var result = Elements[0]; + Elements[0] = Elements[Elements.Count - 1]; + Elements.RemoveAt(Elements.Count - 1); + + ReCalculateDown(); + + return result; + } + + public void Add(TValue element) + { + Elements.Add(element); + ReCalculateUp(); + } + + private void ReCalculateDown() + { + int index = 0; + while (HasLeftChild(index)) + { + var biggerIndex = GetLeftChildIndex(index); + if (HasRightChild(index) && + _comparer.Compare(GetRightChild(index), GetLeftChild(index)) > 0) + { + biggerIndex = GetRightChildIndex(index); + } + + if (_comparer.Compare(Elements[biggerIndex], Elements[index]) < 0) + { + break; + } + + Swap(biggerIndex, index); + index = biggerIndex; + } + } + + private void ReCalculateUp() + { + var index = Elements.Count - 1; + while (!IsRoot(index) && _comparer.Compare(Elements[index], GetParent(index)) > 0) + { + var parentIndex = GetParentIndex(index); + Swap(parentIndex, index); + index = parentIndex; + } + } + +} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs new file mode 100644 index 0000000000..3fdc1485ca --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs @@ -0,0 +1,36 @@ +using PChecker.Generator; +using PChecker.Feedback; + +namespace PChecker.SystematicTesting.Strategies.Feedback; + + +internal class TwoStageFeedbackStrategy : FeedbackGuidedStrategy + where TInput: IInputGenerator + where TSchedule: IScheduleGenerator +{ + + private int _numScheduleMutationWithoutNewSaved = 0; + + // This number should be less than `FeedbackGuidedStrategy._maxMutationsWithoutNewSaved` + private readonly int _maxScheduleMutationsWithoutNewSaved = 25; + public TwoStageFeedbackStrategy(CheckerConfiguration checkerConfiguration, TInput input, TSchedule schedule) : base(checkerConfiguration, input, schedule) + { + } + + protected override StrategyGenerator MutateGenerator(StrategyGenerator prev) + { + if (_numScheduleMutationWithoutNewSaved > _maxScheduleMutationsWithoutNewSaved) + { + _numScheduleMutationWithoutNewSaved = 0; + return new StrategyGenerator( + Generator.InputGenerator.Mutate(), + // do not mutate schedule to save time? + Generator.ScheduleGenerator.Copy() + ); + } + return new StrategyGenerator( + Generator.InputGenerator.Copy(), + Generator.ScheduleGenerator.Mutate() + ); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs new file mode 100644 index 0000000000..add66d209d --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; + +public class Chain +{ + public List Ops = new(); + public int CurrentIndex = 0; + + public OperationWithId CurrentOp() { + if (CurrentIndex < Ops.Count) + { + return Ops[CurrentIndex]; + } + return null; + } + + public List SliceSuccessors(int op) + { + var index = Ops.FindIndex(it => it.Id == op); + return Ops.GetRange(index, Ops.Count - index - 1); + } + + public void AppendAll(List ops) + { + Ops.AddRange(ops); + } + + public void RemoveAll(List ops) + { + Ops.RemoveAll(it => ops.Contains(it)); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs new file mode 100644 index 0000000000..343f83979c --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs @@ -0,0 +1,3 @@ +namespace PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; + +public record OperationWithId(string Sender, string Receiver, int Loc, int Id); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs new file mode 100644 index 0000000000..46982c4d69 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs @@ -0,0 +1,8 @@ +using PChecker.Actors.Logging; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; + +public class VectorClockWrapper +{ + public VectorClockGenerator CurrentVC; +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs new file mode 100644 index 0000000000..dd6c0fd540 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.Actors.Logging; +using PChecker.IO.Debugging; +using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + + +internal class PCTCPScheduler: PrioritizedScheduler +{ + public readonly PriorizationProvider Provider; + + private int ScheduledSteps; + public readonly int MaxPrioritySwitchPoints; + public int ScheduleLength; + private int _nextPriorityChangePoint; + private int _numSwitchPointsLeft; + private int _nextOperationId = 0; + private Dictionary _chainedOperations = new(); + private List _chains = new(); + private Dictionary> _vectorClockMap = new(); + private Dictionary> _predMap = new(); + private Dictionary _operationMap = new(); + private Dictionary _chainMap = new(); + private Dictionary _nextOperationMap = new(); + internal VectorClockWrapper VcWrapper; + + public PCTCPScheduler(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider, + VectorClockWrapper vcWrapper) + { + Provider = provider; + ScheduledSteps = 0; + ScheduleLength = scheduleLength; + MaxPrioritySwitchPoints = maxPrioritySwitchPoints; + _numSwitchPointsLeft = maxPrioritySwitchPoints; + VcWrapper = vcWrapper; + + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); + } + + public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + next = null; + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) + { + if (_nextPriorityChangePoint == ScheduledSteps) + { + MovePriorityChangePointForward(); + } + return false; + } + + var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); + if (next is null) + { + next = highestEnabledOp; + } + + ScheduledSteps++; + return true; + } + + private void OnNewOperation(AsyncOperation operation) + { + Dictionary vc; + if (VcWrapper.CurrentVC.ContextVcMap.ContainsKey(operation.Name)) + { + vc = VcWrapper.CurrentVC + .ContextVcMap[operation.Name] + .ToDictionary(entry => entry.Key, entry => entry.Value); + } + else + { + vc = new(); + } + + OperationWithId op; + if (operation.Type == AsyncOperationType.Send) + { + op = new OperationWithId(operation.Name, operation.LastSentReceiver, operation + .LastEvent!.Loc, _nextOperationId++); + } + else + { + op = new OperationWithId(operation.Name, "", 0, _nextOperationId++); + } + + _vectorClockMap[op.Id] = vc; + _chainedOperations[operation.Name] = op.Id; + _operationMap[op.Id] = op; + _predMap[op.Id] = new(); + + for (int i = 0; i < op.Id - 1; i++) + { + if (IsLT(_vectorClockMap[i], _vectorClockMap[op.Id])) + { + _predMap[op.Id].Add(i); + } + } + + if (!PlaceInChains(op)) + { + Chain newChain = new(); + newChain.Ops.Add(op); + _chainMap[op.Id] = newChain; + if (_chains.Count == 0) + { + _chains.Add(newChain); + } + else + { + var index = Provider.AssignPriority(_chains.Count); + _chains.Insert(index, newChain); + } + } + } + + private bool PlaceInChains(OperationWithId op) + { + // var currentQ = _chains. + for (int i = 0; i < _chains.Count; i++) + { + var chain = _chains[i]; + if (chain.Ops.Count > 0) + { + var tail = chain.Ops.Last(); + if (IsLT(_vectorClockMap[tail.Id], _vectorClockMap[op.Id])) + { + chain.Ops.Add(op); + _nextOperationMap[tail.Id] = op.Id; + _chainMap[op.Id] = chain; + return true; + } + } + } + return false; + } + + bool IsLT(Dictionary vc1, Dictionary vc2) + { + bool hasLess = false; + foreach (var key in vc1.Keys.Union(vc2.Keys)) + { + var op1 = vc1.GetValueOrDefault(key, 0); + var op2 = vc2.GetValueOrDefault(key, 0); + if (op1 >= op2) + { + return false; + } + + if (op1 < op2) + { + hasLess = true; + } + } + return hasLess; + } + + private void MovePriorityChangePointForward() + { + _nextPriorityChangePoint += 1; + Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); + } + private OperationWithId GetHighestPriorityEnabledOperation(IEnumerable choices) + { + OperationWithId highestPriorityOp = null; + int currentPriority = Int32.MaxValue; + int currentChainIndex = Int32.MaxValue; + foreach (var op in choices) + { + var id = _chainedOperations[op.Name]; + var chain = _chainMap[id]; + var priotiy = _chains.IndexOf(chain); + if (priotiy < currentPriority) + { + highestPriorityOp = _operationMap[id]; + currentPriority = priotiy; + currentChainIndex = chain.Ops.IndexOf(highestPriorityOp); + } + + if (priotiy == currentPriority) + { + var index = chain.Ops.IndexOf(_operationMap[id]); + if (index < currentChainIndex) + { + highestPriorityOp = _operationMap[id]; + currentChainIndex = index; + } + } + } + return highestPriorityOp; + } + + private (int, int, Dictionary) FindReducingSequence() + { + var queue = new Queue(); + foreach (var chain in _chains) + { + if (chain.Ops.Count > 0) + { + queue.Enqueue(chain.Ops.First().Id); + } + } + + var pairs = new Dictionary(); + + while (queue.Count > 0) + { + var opId = queue.Dequeue(); + foreach (var chain in _chains) + { + if (chain == _chainMap[opId]) continue; + if (chain.Ops.Count <= 0) continue; + if (IsLT(_vectorClockMap[chain.Ops.Last().Id], _vectorClockMap[opId])) + { + return (chain.Ops.Last().Id, opId, pairs); + } + } + var temp = _predMap[opId].Where(it => _nextOperationMap.ContainsKey(it) + && !pairs.ContainsKey(_nextOperationMap[it])) + .ToList(); + foreach (var predOp in temp) + { + queue.Enqueue(_nextOperationMap[predOp]); + pairs[_nextOperationMap[predOp]] = (predOp, opId); + } + + } + return (-1, -1, pairs); + } + + private void ReduceChains() + { + var (pred, op, pairs) = FindReducingSequence(); + + if (pred == -1) return; + + do + { + var predChain = _chainMap[pred]; + var opChain = _chainMap[op]; + var ids = opChain.SliceSuccessors(op); + predChain.Ops.AddRange(ids); + opChain.Ops.RemoveAll(it => ids.Contains(it)); + foreach (var id in ids) + { + _chainMap[id.Id] = predChain; + } + + _nextOperationMap[pred] = op; + + if (opChain.Ops.Count > 0) + { + _nextOperationMap.Remove(opChain.Ops.Last().Id); + } + + if (!pairs.ContainsKey(op)) + { + _chains.Remove(opChain); + break; + } + pred = pairs[op].Item1; + op = pairs[op].Item2; + } while (true); + + } + + /// + /// Returns the prioritized operation. + /// + private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) + { + bool newOpAdded = false; + foreach (var op in ops.Where(op => !_chainedOperations.ContainsKey(op.Name))) + { + OnNewOperation(op); + newOpAdded = true; + } + + if (newOpAdded) + { + ReduceChains(); + } + + + var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); + if (_nextPriorityChangePoint == ScheduledSteps) + { + if (ops.Count == 1) + { + MovePriorityChangePointForward(); + } + else + { + var chain = _chainMap[prioritizedSchedulable.Id]; + _chains.Remove(chain); + _chains.Add(chain); + + _numSwitchPointsLeft -= 1; + // Update the next priority change point. + if (_numSwitchPointsLeft > 0) + { + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()) + ScheduledSteps; + } + } + } + + AsyncOperation scheduledOperation = null; + if (prioritizedSchedulable != null) + { + scheduledOperation = ops.First(it => it.Name == prioritizedSchedulable.Sender); + _chainedOperations.Remove(scheduledOperation.Name); + Debug.WriteLine(" scheduled operation: " + scheduledOperation.Name); + } + + return scheduledOperation; + } + + public void Reset() + { + ScheduleLength = 0; + ScheduledSteps = 0; + _chainedOperations.Clear(); + _vectorClockMap.Clear(); + _chains.Clear(); + _nextOperationMap.Clear(); + _nextOperationId = 0; + } + + /// + public virtual bool PrepareForNextIteration() + { + ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); + ScheduledSteps = 0; + _nextOperationId = 0; + _numSwitchPointsLeft = MaxPrioritySwitchPoints; + _chainedOperations.Clear(); + _vectorClockMap.Clear(); + _chains.Clear(); + _nextOperationMap.Clear(); + _chainMap.Clear(); + + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); + return true; + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs new file mode 100644 index 0000000000..c0263c68e1 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.SystematicTesting.Operations; +using Debug = PChecker.IO.Debugging.Debug; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class PCTScheduler: PrioritizedScheduler +{ + public readonly PriorizationProvider Provider; + + /// + /// The number of scheduled steps. + /// + private int ScheduledSteps; + + /// + /// Max number of priority switch points. + /// + public readonly int MaxPrioritySwitchPoints; + + /// + /// Approximate length of the schedule across all iterations. + /// + public int ScheduleLength; + + /// + /// List of prioritized operations. + /// + private readonly List PrioritizedOperations; + + private int _nextPriorityChangePoint; + private int _numSwitchPointsLeft; + + /// + /// Initializes a new instance of the class. + /// + public PCTScheduler(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider) + { + Provider = provider; + ScheduledSteps = 0; + ScheduleLength = scheduleLength; + MaxPrioritySwitchPoints = maxPrioritySwitchPoints; + PrioritizedOperations = new List(); + _numSwitchPointsLeft = maxPrioritySwitchPoints; + + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); + } + + public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + ScheduledSteps++; + next = null; + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) + { + if (_nextPriorityChangePoint == ScheduledSteps) + { + MovePriorityChangePointForward(); + } + return false; + } + + var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); + if (next is null) + { + next = highestEnabledOp; + } + + return true; + } + + private void MovePriorityChangePointForward() + { + _nextPriorityChangePoint += 1; + Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); + } + private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) + { + AsyncOperation prioritizedOp = null; + foreach (var entity in PrioritizedOperations) + { + if (choices.Any(m => m == entity)) + { + prioritizedOp = entity; + break; + } + } + + return prioritizedOp; + } + + + /// + /// Returns the prioritized operation. + /// + private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) + { + if (PrioritizedOperations.Count == 0) + { + PrioritizedOperations.Add(current); + } + + foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) + { + var mIndex = Provider.AssignPriority(PrioritizedOperations.Count); + PrioritizedOperations.Insert(mIndex, op); + Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); + } + + + var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); + if (_nextPriorityChangePoint == ScheduledSteps) + { + if (ops.Count == 1) + { + MovePriorityChangePointForward(); + } + else + { + PrioritizedOperations.Remove(prioritizedSchedulable); + PrioritizedOperations.Add(prioritizedSchedulable); + Debug.WriteLine(" Operation '{0}' changes to lowest priority.", prioritizedSchedulable); + + _numSwitchPointsLeft -= 1; + // Update the next priority change point. + if (_numSwitchPointsLeft > 0) + { + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()) + ScheduledSteps; + } + + } + } + + if (Debug.IsEnabled) + { + Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); + Debug.Write(" Priority list: "); + for (var idx = 0; idx < PrioritizedOperations.Count; idx++) + { + if (idx < PrioritizedOperations.Count - 1) + { + Debug.Write("'{0}', ", PrioritizedOperations[idx]); + } + else + { + Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); + } + } + } + + return ops.First(op => op.Equals(prioritizedSchedulable)); + } + + public void Reset() + { + ScheduleLength = 0; + ScheduledSteps = 0; + PrioritizedOperations.Clear(); + } + + /// + public virtual bool PrepareForNextIteration() + { + ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); + ScheduledSteps = 0; + _numSwitchPointsLeft = MaxPrioritySwitchPoints; + + PrioritizedOperations.Clear(); + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); + return true; + } +} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs new file mode 100644 index 0000000000..589ae13099 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.Feedback; +using PChecker.IO.Debugging; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class POSScheduler: PrioritizedScheduler +{ + internal readonly PriorizationProvider Provider; + + /// + /// List of prioritized operations. + /// + private readonly List PrioritizedOperations; + + public ConflictOpMonitor? ConflictOpMonitor; + + /// + /// Initializes a new instance of the class. + /// + public POSScheduler(PriorizationProvider provider, ConflictOpMonitor? monitor) + { + Provider = provider; + PrioritizedOperations = new List(); + ConflictOpMonitor = monitor; + } + + public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + next = null; + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) + { + return false; + } + + var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); + next = highestEnabledOp; + if (next.Type == AsyncOperationType.Send) + { + ResetPriorities(next, enabledOperations); + } + return true; + } + + void ResetPriorities(AsyncOperation next, IEnumerable ops) + { + foreach (var op in ops) + { + if (op.Type == AsyncOperationType.Send) + { + if (op.LastSentReceiver == next.LastSentReceiver) + { + PrioritizedOperations.Remove(op); + } + } + } + PrioritizedOperations.Remove(next); + } + + private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) + { + AsyncOperation prioritizedOp = null; + foreach (var entity in PrioritizedOperations) + { + if (choices.Any(m => m == entity)) + { + prioritizedOp = entity; + break; + } + } + + return prioritizedOp; + } + + /// + /// Returns the prioritized operation. + /// + private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) + { + if (PrioritizedOperations.Count == 0) + { + PrioritizedOperations.Add(current); + } + + foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) + { + var mIndex = Provider.AssignPriority(PrioritizedOperations.Count); + PrioritizedOperations.Insert(mIndex, op); + Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); + } + + if (FindNonRacingOperation(ops, out var next)) + { + return next; + } + + var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); + + if (Debug.IsEnabled) + { + Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); + Debug.Write(" Priority list: "); + for (var idx = 0; idx < PrioritizedOperations.Count; idx++) + { + if (idx < PrioritizedOperations.Count - 1) + { + Debug.Write("'{0}', ", PrioritizedOperations[idx]); + } + else + { + Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); + } + } + } + + return ops.First(op => op.Equals(prioritizedSchedulable)); + } + + private bool FindNonRacingOperation(IEnumerable ops, out AsyncOperation next) + { + var nonRacingOps = ops.Where(op => op.Type != AsyncOperationType.Send); + if (!nonRacingOps.Any() && ConflictOpMonitor != null) + { + nonRacingOps = ops.Where(op => !ConflictOpMonitor.IsConflictingOp(op)); + } + + if (!nonRacingOps.Any()) + { + next = null; + return false; + } + if (!nonRacingOps.Skip(1).Any()) + { + next = nonRacingOps.First(); + return true; + } + next = GetHighestPriorityEnabledOperation(nonRacingOps); + return true; + } + + public void Reset() + { + PrioritizedOperations.Clear(); + } + + /// + public virtual bool PrepareForNextIteration() + { + PrioritizedOperations.Clear(); + return true; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs new file mode 100644 index 0000000000..6968e1c6ee --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs @@ -0,0 +1,48 @@ +using PChecker.Feedback; +using PChecker.Random; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class POSStrategy: POSScheduler, ISchedulingStrategy +{ + /// + /// Random value generator. + /// + private readonly IRandomValueGenerator RandomValueGenerator; + + public POSStrategy(int maxSteps, ConflictOpMonitor? monitor, IRandomValueGenerator random) + : base(new RandomPriorizationProvider(random), monitor) + { + } + + public bool GetNextBooleanChoice(AsyncOperation current, int maxValue, out bool next) + { + throw new System.NotImplementedException(); + } + + public bool GetNextIntegerChoice(AsyncOperation current, int maxValue, out int next) + { + throw new System.NotImplementedException(); + } + + public int GetScheduledSteps() + { + throw new System.NotImplementedException(); + } + + public bool HasReachedMaxSchedulingSteps() + { + throw new System.NotImplementedException(); + } + + public bool IsFair() + { + throw new System.NotImplementedException(); + } + + public string GetDescription() + { + throw new System.NotImplementedException(); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs new file mode 100644 index 0000000000..7c38a0629c --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal interface PrioritizedScheduler +{ + public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next); + public void Reset(); + public bool PrepareForNextIteration(); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs new file mode 100644 index 0000000000..72660d7f97 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.Feedback; +using PChecker.IO.Debugging; +using PChecker.Random; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic +{ + /// + /// A priority-based probabilistic scheduling strategy. + /// + /// + /// This strategy is described in the following paper: + /// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/asplos277-pct.pdf + /// + internal class PrioritizedSchedulingStrategy: ISchedulingStrategy + { + + /// + /// Random value generator. + /// + private readonly IRandomValueGenerator RandomValueGenerator; + + /// + /// The maximum number of steps to schedule. + /// + private readonly int MaxScheduledSteps; + + private int _scheduledSteps; + + private PrioritizedScheduler _scheduler; + + /// + /// Initializes a new instance of the class. + /// + public PrioritizedSchedulingStrategy(int maxSteps, IRandomValueGenerator random, PrioritizedScheduler scheduler) + { + RandomValueGenerator = random; + MaxScheduledSteps = maxSteps; + _scheduler = scheduler; + } + + /// + public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + _scheduledSteps++; + return _scheduler.GetNextOperation(current, ops, out next); + } + + /// + public bool GetNextBooleanChoice(AsyncOperation current, int maxValue, out bool next) + { + next = false; + if (RandomValueGenerator.Next(maxValue) == 0) + { + next = true; + } + + _scheduledSteps++; + + return true; + } + + /// + public bool GetNextIntegerChoice(AsyncOperation current, int maxValue, out int next) + { + next = RandomValueGenerator.Next(maxValue); + _scheduledSteps++; + return true; + } + + /// + public int GetScheduledSteps() => _scheduledSteps; + + /// + public bool HasReachedMaxSchedulingSteps() + { + if (MaxScheduledSteps == 0) + { + return false; + } + + return _scheduledSteps >= MaxScheduledSteps; + } + + /// + public bool IsFair() => false; + + /// + public string GetDescription() + { + var text = $"pct[seed '" + RandomValueGenerator.Seed + "']"; + return text; + } + + public bool PrepareForNextIteration() { + _scheduledSteps = 0; + return _scheduler.PrepareForNextIteration(); + } + + + /// + public void Reset() + { + _scheduledSteps = 0; + _scheduler.Reset(); + } + } +} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs new file mode 100644 index 0000000000..d4cb446516 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs @@ -0,0 +1,7 @@ +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +public interface PriorizationProvider +{ + public int AssignPriority(int numOps); + public double SwitchPointChoice(); +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs new file mode 100644 index 0000000000..3894b96fc8 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs @@ -0,0 +1,243 @@ + + +using System.Collections.Generic; +using System.Linq; +using PChecker.Feedback; +using PChecker.SystematicTesting.Operations; +using PChecker.Generator.Mutator; +using PChecker.IO.Debugging; +using System; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class PriorizationSchedulingBase +{ + public readonly PriorizationProvider Provider; + + /// + /// The number of scheduled steps. + /// + private int ScheduledSteps; + + /// + /// Max number of priority switch points. + /// + public readonly int MaxPrioritySwitchPoints; + + /// + /// Approximate length of the schedule across all iterations. + /// + public int ScheduleLength; + + /// + /// List of prioritized operations. + /// + private readonly List PrioritizedOperations; + + public ConflictOpMonitor? ConflictOpMonitor; + private int _nextPriorityChangePoint; + private int _numSwitchPointsLeft; + + /// + /// Initializes a new instance of the class. + /// + public PriorizationSchedulingBase(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider, ConflictOpMonitor? monitor) + { + Provider = provider; + ScheduledSteps = 0; + ScheduleLength = scheduleLength; + MaxPrioritySwitchPoints = maxPrioritySwitchPoints; + PrioritizedOperations = new List(); + ConflictOpMonitor = monitor; + _numSwitchPointsLeft = maxPrioritySwitchPoints; + + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); + } + + public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + ScheduledSteps++; + next = null; + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) + { + if (ConflictOpMonitor == null && _nextPriorityChangePoint == ScheduledSteps) + { + MovePriorityChangePointForward(); + } + return false; + } + + var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); + if (next is null) + { + next = highestEnabledOp; + } + if (ConflictOpMonitor != null) + { + ResetPriorities(next, enabledOperations); + } + + return true; + } + + public void ResetPriorities(AsyncOperation next, IEnumerable ops) + { + foreach (var op in ops) + { + if (op != next && ConflictOpMonitor.IsRacing(next, op)) + { + PrioritizedOperations.Remove(op); + } + } + PrioritizedOperations.Remove(next); + } + + private void MovePriorityChangePointForward() + { + _nextPriorityChangePoint += 1; + Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); + } + private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) + { + AsyncOperation prioritizedOp = null; + foreach (var entity in PrioritizedOperations) + { + if (choices.Any(m => m == entity)) + { + prioritizedOp = entity; + break; + } + } + + return prioritizedOp; + } + + + + /// + /// Returns the prioritized operation. + /// + private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) + { + if (PrioritizedOperations.Count == 0) + { + PrioritizedOperations.Add(current); + } + + foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) + { + var mIndex = Provider.AssignPriority(PrioritizedOperations.Count); + PrioritizedOperations.Insert(mIndex, op); + Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); + } + + if (ConflictOpMonitor != null && FindNonRacingOperation(ops, out var next)) + { + return next; + } + + var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); + if (ConflictOpMonitor == null && _nextPriorityChangePoint == ScheduledSteps) + { + if (ops.Count == 1) + { + MovePriorityChangePointForward(); + } + else + { + PrioritizedOperations.Remove(prioritizedSchedulable); + PrioritizedOperations.Add(prioritizedSchedulable); + Debug.WriteLine(" Operation '{0}' changes to lowest priority.", prioritizedSchedulable); + + _numSwitchPointsLeft -= 1; + // Update the next priority change point. + if (_numSwitchPointsLeft > 0) + { + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()) + ScheduledSteps; + } + + } + } + + if (Debug.IsEnabled) + { + Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); + Debug.Write(" Priority list: "); + for (var idx = 0; idx < PrioritizedOperations.Count; idx++) + { + if (idx < PrioritizedOperations.Count - 1) + { + Debug.Write("'{0}', ", PrioritizedOperations[idx]); + } + else + { + Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); + } + } + } + + return ops.First(op => op.Equals(prioritizedSchedulable)); + } + + private bool FindNonRacingOperation(IEnumerable ops, out AsyncOperation next) + { + var nonRacingOps = ops.Where(op => op.Type != AsyncOperationType.Send); + if (!nonRacingOps.Any()) + { + var sendOps = ops.Where(op => op.Type == AsyncOperationType.Send); + nonRacingOps = ops.Where(op => !ConflictOpMonitor.IsConflictingOp(op)); + } + + if (!nonRacingOps.Any()) + { + next = null; + return false; + } + else if (!nonRacingOps.Skip(1).Any()) + { + next = nonRacingOps.First(); + return true; + } + else + { + next = GetHighestPriorityEnabledOperation(nonRacingOps); + return true; + } + + } + + public void Reset() + { + ScheduleLength = 0; + ScheduledSteps = 0; + PrioritizedOperations.Clear(); + } + + /// + public virtual bool PrepareForNextIteration() + { + ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); + ScheduledSteps = 0; + _numSwitchPointsLeft = MaxPrioritySwitchPoints; + + PrioritizedOperations.Clear(); + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); + return true; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs index ba90bcf613..4b23127301 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs @@ -3,17 +3,20 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using PChecker.Feedback; using System.Text; using PChecker.Random; using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Feedback; namespace PChecker.SystematicTesting.Strategies.Probabilistic { /// /// A probabilistic scheduling strategy that uses Q-learning. /// - internal class QLearningStrategy : RandomStrategy + internal class QLearningStrategy : RandomStrategy, IFeedbackGuidedStrategy { /// /// Map from program states to a map from next operations to their quality values. @@ -78,11 +81,13 @@ internal class QLearningStrategy : RandomStrategy /// private int Epochs; + private bool _diversityFeedback; + /// /// Initializes a new instance of the class. /// It uses the specified random number generator. /// - public QLearningStrategy(int maxSteps, IRandomValueGenerator random) + public QLearningStrategy(int maxSteps, IRandomValueGenerator random, bool diversityFeedback) : base(maxSteps, random) { this.OperationQTable = new Dictionary>(); @@ -97,6 +102,7 @@ public QLearningStrategy(int maxSteps, IRandomValueGenerator random) this.FailureInjectionReward = -1000; this.BasicActionReward = -1; this.Epochs = 0; + _diversityFeedback = diversityFeedback; } /// @@ -148,7 +154,6 @@ public override bool GetNextIntegerChoice(AsyncOperation current, int maxValue, /// public override bool PrepareForNextIteration() { - this.LearnQValues(); this.ExecutionPath.Clear(); this.PreviousOperation = 0; this.Epochs++; @@ -358,18 +363,69 @@ private void InitializeIntegerChoiceQValues(int state, int maxValue) } } - /// - /// Learn Q values using data from the current execution. - /// - private void LearnQValues() + private readonly HashSet _visitedTimelines = new(); + private readonly List> _savedTimelines = new(); + private int ComputeDiversity(int timeline, List hash) { - var pathBuilder = new StringBuilder(); + if (!_visitedTimelines.Add(timeline)) + { + return 0; + } + + if (_savedTimelines.Count == 0) + { + return 20; + } + + var maxSim = int.MinValue; + foreach (var record in _savedTimelines) + { + var similarity = 0; + for (int i = 0; i < hash.Count; i++) + { + if (hash[i] == record[i]) + { + similarity += 1; + } + } + + maxSim = Math.Max(maxSim, similarity); + } + + + return (hash.Count - maxSim) * 10 + 20; + } + + public void ObserveRunningResults(EventPatternObserver patternObserver, ControlledRuntime runtime) + { + var timelineHash = runtime.TimelineObserver.GetTimelineHash(); + var timelineMinhash = runtime.TimelineObserver.GetTimelineMinhash(); + + int priority = 1; + + if (_diversityFeedback) + { + int diversityScore = ComputeDiversity(timelineHash, timelineMinhash); + if (patternObserver == null) + { + priority = diversityScore; + } + else + { + int coverageResult = patternObserver.ShouldSave(); + priority = diversityScore / coverageResult; + } + + if (priority != 0) + { + _savedTimelines.Add(timelineMinhash); + } + priority += 1; + } - int idx = 0; var node = this.ExecutionPath.First; while (node != null && node.Next != null) { - pathBuilder.Append($"{node.Value.op},"); var (_, _, state) = node.Value; var (nextOp, nextType, nextState) = node.Next.Value; @@ -386,8 +442,8 @@ private void LearnQValues() // Compute the reward. Program states that are visited with higher frequency result into lesser rewards. var freq = this.TransitionFrequencies[nextState]; - double reward = (nextType == AsyncOperationType.InjectFailure ? - this.FailureInjectionReward : this.BasicActionReward) * freq; + double reward = ((nextType == AsyncOperationType.InjectFailure ? + this.FailureInjectionReward : this.BasicActionReward) * freq) / priority; if (reward > 0) { // The reward has underflowed. @@ -407,8 +463,16 @@ private void LearnQValues() (this.LearningRate * (reward + (this.Gamma * maxQ))); node = node.Next; - idx++; } } + + public int TotalSavedInputs() + { + return 0; + } + + public void DumpStats(TextWriter writer) + { + } } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs new file mode 100644 index 0000000000..11d27c2e26 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.Feedback; +using PChecker.Random; +using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class ScheduleRecord +{ + public int NumVisit {get; set; } + public int FuzzLevel {get; set; } + public int Priority {get; set;} + public AbstractSchedule Schedule; + + public ScheduleRecord(int numVisit, int fuzzLevel, AbstractSchedule schedule) { + NumVisit = numVisit; + FuzzLevel = fuzzLevel; + Schedule = schedule; + } + +} +internal class RFFScheduler: PrioritizedScheduler +{ + // public List<(AbstractSchedule, )> savedSchedules = new(); + public Dictionary TraceRecords = new(); + public int SavedTraces = 0; + public int TotalExec = 1; + int Skip = 0; + int Adj = 1; + int SchedNonDets = 0; + int parentIndex = 0; + int count = 0; + internal AbstractSchedule currentSchedule; + internal AbstractScheduleObserver observer; + internal List savedSchedules = new(); + public IRandomValueGenerator random; + private POSScheduler _posScheduler; + + public RFFScheduler(IRandomValueGenerator random, ConflictOpMonitor monitor, AbstractScheduleObserver observer) + { + this.observer = observer; + this.random = random; + currentSchedule = new AbstractSchedule(new HashSet()); + observer.OnNewAbstractSchedule(currentSchedule); + _posScheduler = new POSScheduler(new RandomPriorizationProvider(random), monitor); + } + + public bool PrepareForNextIteration() + { + _posScheduler.PrepareForNextIteration(); + // We should always check novelty to update global states. + if (observer.CheckNoveltyAndUpdate() || observer.CheckAbstractTimelineSatisfied()) + { + int traceHash = observer.GetTraceHash(); + + if (!TraceRecords.ContainsKey(traceHash)) + { + TraceRecords[traceHash] = new ScheduleRecord(0, 0, currentSchedule); + savedSchedules.Add(TraceRecords[traceHash]); + } + var record = TraceRecords[traceHash]; + record.NumVisit += 1; + + int u = TotalExec / (SavedTraces + Adj); + + int factor; + if (record.NumVisit <= u) + { + if (record.FuzzLevel < 31) + { + record.FuzzLevel += 1; + } + + factor = (1 << record.FuzzLevel) / u; + Skip = 0; + } + else + { + factor = 0; + Skip += 1; + if (Skip >= SchedNonDets) + { + Skip = 0; + Adj += 1; + } + } + int PerfScore = Math.Min(factor * 1, 50); + record.Priority = PerfScore; + SavedTraces += 1; + } + TotalExec += 1; + + + if (parentIndex == -1 && savedSchedules.Count > 0) + { + parentIndex = 0; + } + + if (parentIndex == -1) + { + currentSchedule = currentSchedule.Mutate(observer.allVisitedConstraints.Keys.ToList(), random); + return true; + } + + if (count < savedSchedules[parentIndex].Priority) + { + currentSchedule = savedSchedules[parentIndex].Schedule.Mutate(observer.allVisitedConstraints.Keys.ToList(), random); + count += 1; + } else { + parentIndex += 1; + if (parentIndex >= savedSchedules.Count) { + parentIndex = 0; + } + count = 0; + } + + observer.OnNewAbstractSchedule(currentSchedule); + return true; + } + + public void Reset() + { + _posScheduler.Reset(); + } + + public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) + { + next = null; + return false; + } + if (enabledOperations.Count == 1) + { + next = enabledOperations[0]; + return true; + } + + var highPrioOps = new List(); + var normalPrioOps = new List(); + var lowPrioOps = new List(); + foreach (var op in enabledOperations) + { + var avoid = observer.ShouldAvoid(op); + var take = observer.ShouldTake(op); + + if (avoid && !take) + { + lowPrioOps.Add(op); + } + else if (!avoid && take) + { + highPrioOps.Add(op); + } + else + { + normalPrioOps.Add(op); + } + + } + + if (highPrioOps.Count > 0) + { + return _posScheduler.GetNextOperation(current, highPrioOps, out next); + } + if (normalPrioOps.Count > 0) + { + return _posScheduler.GetNextOperation(current, normalPrioOps, out next); + } + return _posScheduler.GetNextOperation(current, lowPrioOps, out next); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs new file mode 100644 index 0000000000..c757366cd4 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs @@ -0,0 +1,27 @@ +using PChecker.Random; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class RandomPriorizationProvider: PriorizationProvider +{ + + /// + /// Random value generator. + /// + private readonly IRandomValueGenerator RandomValueGenerator; + + public RandomPriorizationProvider(IRandomValueGenerator generator) + { + RandomValueGenerator = generator; + } + public int AssignPriority(int numOps) + { + return RandomValueGenerator.Next(numOps) + 1; + } + + public double SwitchPointChoice() + { + return RandomValueGenerator.NextDouble(); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs index 7bd654b618..eefa755621 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs @@ -48,7 +48,11 @@ public virtual bool GetNextOperation(AsyncOperation current, IEnumerable 1) { + idx = RandomValueGenerator.Next(enabledOperations.Count); + } + next = enabledOperations[idx]; ScheduledSteps++; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs new file mode 100644 index 0000000000..d4b3a8a0c1 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.SystematicTesting.Strategies; + +public class Utils +{ + internal static List FindHighPriorityOperations(IEnumerable ops, HashSet interestingEvents) + { + var highOps = ops.Where(it => + + { + if (it.Status == AsyncOperationStatus.Enabled) + { + if (it is ActorOperation act) + { + if (act.Type == AsyncOperationType.Send) + { + if (act.LastEvent != null) + { + return !interestingEvents.Contains(act.LastEvent.GetType()); + } + return false; + } + } + return true; + } + return false; + } + ).ToList(); + if (highOps.Count != 0) + { + return highOps; + } + return ops.Where( + op => + { + return op.Status is AsyncOperationStatus.Enabled; + } + ).ToList(); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs index 81ee7583a6..24fd70057b 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs @@ -100,6 +100,19 @@ public class TestReport [DataMember] public HashSet InternalErrors { get; internal set; } + + /// + /// Set of hashes of timelines discovered by the scheduler. + /// + [DataMember] + public HashSet ExploredTimelines = new(); + + /// + /// Number of schedulings that satisfies the pattern. + /// + [DataMember] + public Dictionary ValidScheduling = new(); + /// /// Lock for the test report. /// diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index 0c7fb0bca2..ed668c03ea 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -17,6 +18,8 @@ using PChecker.Actors; using PChecker.Actors.Logging; using PChecker.Coverage; +using PChecker.Feedback; +using PChecker.Generator; using PChecker.IO; using PChecker.IO.Debugging; using PChecker.IO.Logging; @@ -24,10 +27,13 @@ using PChecker.Runtime; using PChecker.SystematicTesting.Strategies; using PChecker.SystematicTesting.Strategies.Exhaustive; +using PChecker.SystematicTesting.Strategies.Feedback; using PChecker.SystematicTesting.Strategies.Probabilistic; +using PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; using PChecker.SystematicTesting.Strategies.Special; using PChecker.SystematicTesting.Traces; using PChecker.Utilities; +using Debug = PChecker.IO.Debugging.Debug; using Task = PChecker.Tasks.Task; namespace PChecker.SystematicTesting @@ -59,6 +65,14 @@ public class TestingEngine /// internal readonly ISchedulingStrategy Strategy; + private EventPatternObserver? _eventPatternObserver; + + private ConflictOpMonitor? _conflictOpMonitor; + + private VectorClockWrapper? _vcWrapper; + + private AbstractScheduleObserver? _abstractScheduleObserver; + /// /// Random value generator used by the scheduling strategies. /// @@ -147,12 +161,18 @@ public class TestingEngine /// private int PrintGuard; + private StreamWriter TimelineFileStream; + + /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(CheckerConfiguration checkerConfiguration) => Create(checkerConfiguration, LoadAssembly(checkerConfiguration.AssemblyToBeAnalyzed)); + private Stopwatch watch; + private bool ShouldEmitTrace; + /// /// Creates a new systematic testing engine. /// @@ -179,17 +199,26 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration, As } TestMethodInfo testMethodInfo = null; + EventPatternObserver eventMatcher = null; try { testMethodInfo = TestMethodInfo.GetFromAssembly(assembly, checkerConfiguration.TestCaseName); Console.Out.WriteLine($".. Test case :: {testMethodInfo.Name}"); + + Type t = assembly.GetType("PImplementation.GlobalFunctions"); + if (checkerConfiguration.PatternSource.Length > 0) + { + var result = t.GetMethod(checkerConfiguration.PatternSource, + BindingFlags.Public | BindingFlags.Static)!; + eventMatcher = new EventPatternObserver(result); + } } catch { Error.ReportAndExit($"Failed to get test method '{checkerConfiguration.TestCaseName}' from assembly '{assembly.FullName}'"); } - return new TestingEngine(checkerConfiguration, testMethodInfo); + return new TestingEngine(checkerConfiguration, testMethodInfo, eventMatcher); } /// @@ -236,13 +265,19 @@ internal TestingEngine(CheckerConfiguration checkerConfiguration, Delegate test) { } + private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo) + : this(checkerConfiguration, testMethodInfo, null) + { + } + /// /// Initializes a new instance of the class. /// - private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo) + private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo, EventPatternObserver observer) { _checkerConfiguration = checkerConfiguration; TestMethodInfo = testMethodInfo; + _eventPatternObserver = observer; Logger = new ConsoleLogger(); ErrorReporter = new ErrorReporter(checkerConfiguration, Logger); @@ -259,12 +294,16 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo CancellationTokenSource = new CancellationTokenSource(); PrintGuard = 1; - + TimelineFileStream = new StreamWriter(checkerConfiguration.OutputDirectory + "timeline.txt"); // Initialize a new instance of JsonVerboseLogs if running in verbose mode. if (checkerConfiguration.IsVerbose) { JsonVerboseLogs = new List>(); } + if (checkerConfiguration.EnableConflictAnalysis || checkerConfiguration.SchedulingStrategy == "rff") + { + _conflictOpMonitor = new ConflictOpMonitor(); + } if (checkerConfiguration.SchedulingStrategy is "replay") { @@ -278,13 +317,38 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "pct") { - Strategy = new PCTStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, checkerConfiguration.StrategyBound, - RandomValueGenerator); + var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, + new RandomPriorizationProvider(RandomValueGenerator)); + Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, + RandomValueGenerator, scheduler); + } + else if (checkerConfiguration.SchedulingStrategy is "pctcp") + { + _vcWrapper = new VectorClockWrapper(); + var scheduler = new PCTCPScheduler(checkerConfiguration.StrategyBound, 0, + new RandomPriorizationProvider(RandomValueGenerator), _vcWrapper); + Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, + RandomValueGenerator, scheduler); + } + else if (checkerConfiguration.SchedulingStrategy is "pos") + { + var scheduler = new POSScheduler(new RandomPriorizationProvider(RandomValueGenerator), _conflictOpMonitor); + Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, + RandomValueGenerator, scheduler); + } + else if (checkerConfiguration.SchedulingStrategy is "rff") + { + _abstractScheduleObserver = new AbstractScheduleObserver(); + var scheduler = new RFFScheduler(RandomValueGenerator, _conflictOpMonitor, _abstractScheduleObserver); + Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, + RandomValueGenerator, scheduler); } else if (checkerConfiguration.SchedulingStrategy is "fairpct") { var prefixLength = checkerConfiguration.MaxUnfairSchedulingSteps; - var prefixStrategy = new PCTStrategy(prefixLength, checkerConfiguration.StrategyBound, RandomValueGenerator); + var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, + new RandomPriorizationProvider(RandomValueGenerator)); + var prefixStrategy = new PrioritizedSchedulingStrategy(prefixLength, RandomValueGenerator, scheduler); var suffixStrategy = new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); Strategy = new ComboStrategy(prefixStrategy, suffixStrategy); } @@ -295,12 +359,43 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "rl") { - Strategy = new QLearningStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator); + Strategy = new QLearningStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, + checkerConfiguration.DiversityBasedPriority); } else if (checkerConfiguration.SchedulingStrategy is "dfs") { Strategy = new DFSStrategy(checkerConfiguration.MaxUnfairSchedulingSteps); } + else if (checkerConfiguration.SchedulingStrategy is "feedback") + { + Strategy = new FeedbackGuidedStrategy( + _checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new RandomScheduleGenerator(checkerConfiguration)); + } + else if (checkerConfiguration.SchedulingStrategy is "2stagefeedback") + { + Strategy = new TwoStageFeedbackStrategy(_checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new RandomScheduleGenerator(checkerConfiguration)); + } + else if (checkerConfiguration.SchedulingStrategy is "feedbackpct") + { + Strategy = new FeedbackGuidedStrategy(_checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new PctScheduleGenerator(checkerConfiguration)); + } + else if (checkerConfiguration.SchedulingStrategy is "feedbackpctcp") + { + _vcWrapper = new VectorClockWrapper(); + Strategy = new FeedbackGuidedStrategy(_checkerConfiguration, new + RandomInputGenerator(checkerConfiguration), new PctcpScheduleGenerator(checkerConfiguration, _vcWrapper)); + } + else if (checkerConfiguration.SchedulingStrategy is "feedbackpos") + { + Strategy = new FeedbackGuidedStrategy( + _checkerConfiguration, + new RandomInputGenerator(checkerConfiguration), + new POSScheduleGenerator(_checkerConfiguration, _conflictOpMonitor)); + } + else if (checkerConfiguration.SchedulingStrategy is "2stagefeedbackpct") + { + Strategy = new TwoStageFeedbackStrategy(_checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new PctScheduleGenerator(checkerConfiguration)); + } else if (checkerConfiguration.SchedulingStrategy is "portfolio") { Error.ReportAndExit("Portfolio testing strategy is only " + @@ -380,6 +475,10 @@ private System.Threading.Tasks.Task CreateTestingTask() var options = string.Empty; if (_checkerConfiguration.SchedulingStrategy is "random" || _checkerConfiguration.SchedulingStrategy is "pct" || + _checkerConfiguration.SchedulingStrategy is "poc" || + _checkerConfiguration.SchedulingStrategy is "feedbackpct" || + _checkerConfiguration.SchedulingStrategy is "feedbackpctcp" || + _checkerConfiguration.SchedulingStrategy is "feedbackpos" || _checkerConfiguration.SchedulingStrategy is "fairpct" || _checkerConfiguration.SchedulingStrategy is "probabilistic" || _checkerConfiguration.SchedulingStrategy is "rl") @@ -394,9 +493,10 @@ _checkerConfiguration.SchedulingStrategy is "probabilistic" || { try { + // Invokes the user-specified initialization method. TestMethodInfo.InitializeAllIterations(); - + watch = Stopwatch.StartNew(); var maxIterations = IsReplayModeEnabled ? 1 : _checkerConfiguration.TestingIterations; int i = 0; while (maxIterations == 0 || i < maxIterations) @@ -496,13 +596,31 @@ private void RunNextIteration(int schedule) try { + ShouldEmitTrace = false; // Creates a new instance of the controlled runtime. runtime = new ControlledRuntime(_checkerConfiguration, Strategy, RandomValueGenerator); + if (_conflictOpMonitor != null) + { + runtime.SendEventMonitors.Add(_conflictOpMonitor); + } + if (_abstractScheduleObserver != null) + { + runtime.SendEventMonitors.Add(_abstractScheduleObserver); + } + if (_eventPatternObserver != null) + { + runtime.RegisterLog(_eventPatternObserver); + } // Always output a json log of the error JsonLogger = new JsonWriter(); runtime.SetJsonLogger(JsonLogger); + if (_vcWrapper != null) + { + _vcWrapper.CurrentVC = JsonLogger.VcGenerator; + } + // If verbosity is turned off, then intercept the program log, and also redirect // the standard output and error streams to a nul logger. if (!_checkerConfiguration.IsVerbose) @@ -530,6 +648,11 @@ private void RunNextIteration(int schedule) callback(schedule); } + if (Strategy is IFeedbackGuidedStrategy strategy) + { + strategy.ObserveRunningResults(_eventPatternObserver, runtime); + } + // Checks that no monitor is in a hot state at termination. Only // checked if no safety property violations have been found. if (!runtime.Scheduler.BugFound) @@ -552,7 +675,7 @@ private void RunNextIteration(int schedule) GatherTestingStatistics(runtime); - if (!IsReplayModeEnabled && TestReport.NumOfFoundBugs > 0) + if (ShouldEmitTrace || (!IsReplayModeEnabled && TestReport.NumOfFoundBugs > 0)) { if (runtimeLogger != null) { @@ -561,7 +684,12 @@ private void RunNextIteration(int schedule) } ConstructReproducableTrace(runtime); + if (_checkerConfiguration.OutputDirectory != null) + { + TryEmitTraces(_checkerConfiguration.OutputDirectory, "trace_0"); + } } + } finally { @@ -572,6 +700,18 @@ private void RunNextIteration(int schedule) Console.SetError(stdErr); } + + if (ShouldPrintIteration(schedule)) + { + var seconds = watch.Elapsed.TotalSeconds; + Logger.WriteLine($"Elapsed: {seconds}, " + + $"# timelines: {TestReport.ExploredTimelines.Count}"); + if (Strategy is IFeedbackGuidedStrategy s) + { + s.DumpStats(Logger); + } + } + if (!IsReplayModeEnabled && _checkerConfiguration.PerformFullExploration && runtime.Scheduler.BugFound) { Logger.WriteLine($"..... Schedule #{schedule + 1} " + @@ -579,9 +719,15 @@ private void RunNextIteration(int schedule) $"[task-{_checkerConfiguration.TestingProcessId}]"); } - // Cleans up the runtime before the next schedule starts. + // Cleans up the runtime before the next iteration starts. + if (_eventPatternObserver != null) + { + runtime.RemoveLog(_eventPatternObserver); + } runtimeLogger?.Dispose(); runtime?.Dispose(); + _eventPatternObserver?.Reset(); + _conflictOpMonitor?.Reset(); } } @@ -611,6 +757,11 @@ public string GetReport() return TestReport.GetText(_checkerConfiguration, "..."); } + public void TryEmitTimeline(TimelineObserver observer) + { + TimelineFileStream.WriteLine(observer.GetTimeline()); + } + /// /// Returns an object where the value null is replaced with "null" /// @@ -700,14 +851,14 @@ public void TryEmitTraces(string directory, string file) JsonSerializer.Serialize(jsonStreamFile, JsonLogger.Logs, jsonSerializerConfig); } - if (Graph != null) + if (Graph != null && !_checkerConfiguration.PerformFullExploration) { var graphPath = directory + file + "_" + index + ".dgml"; Graph.SaveDgml(graphPath, true); Logger.WriteLine($"..... Writing {graphPath}"); } - if (!_checkerConfiguration.PerformFullExploration) + if (!_checkerConfiguration.PerformFullExploration || ShouldEmitTrace) { // Emits the reproducable trace, if it exists. if (!string.IsNullOrEmpty(ReproducableTrace)) @@ -859,12 +1010,36 @@ private void GatherTestingStatistics(ControlledRuntime runtime) report.CoverageInfo.CoverageGraph = Graph; } - var coverageInfo = runtime.GetCoverageInfo(); - report.CoverageInfo.Merge(coverageInfo); - TestReport.Merge(report); + int shouldSave = 1; + + if (_eventPatternObserver != null) + { + shouldSave = _eventPatternObserver.ShouldSave(); + TestReport.ValidScheduling.TryAdd(shouldSave, 0); + TestReport.ValidScheduling[shouldSave] += 1; - // Also save the graph snapshot of the last schedule, if there is one. - Graph = coverageInfo.CoverageGraph; + } + + if (shouldSave == 1) + { + var coverageInfo = runtime.GetCoverageInfo(); + report.CoverageInfo.Merge(coverageInfo); + TestReport.Merge(report); + + if (TestReport.ExploredTimelines.Add(runtime.TimelineObserver.GetTimelineHash())) + { + // if (_checkerConfiguration.IsVerbose) + // { + // Logger.WriteLine($"... New timeline observed: {runtime.TimelineObserver.GetTimeline()}"); + // } + TryEmitTimeline(runtime.TimelineObserver); + ShouldEmitTrace = true; + } + // Also save the graph snapshot of the last iteration, if there is one. + Graph = coverageInfo.CoverageGraph; + // Also save the graph snapshot of the last schedule, if there is one. + Graph = coverageInfo.CoverageGraph; + } } /// @@ -970,6 +1145,10 @@ private bool ShouldPrintIteration(int schedule) var count = schedule.ToString().Length - 1; var guard = "1" + (count > 0 ? string.Concat(Enumerable.Repeat("0", count)) : string.Empty); PrintGuard = int.Parse(guard); + if (PrintGuard > 1000) + { + PrintGuard = 1000; + } } return schedule % PrintGuard == 0; diff --git a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs index 69e5343d4a..fa2bd5fb1a 100644 --- a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs +++ b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs @@ -22,6 +22,8 @@ public class CSharpCodeGenerator : ICodeGenerator /// public bool HasCompilationStage => true; + private int _sendEventIndex = 0; + public void Compile(ICompilerConfiguration job) { var csprojName = $"{job.ProjectName}.csproj"; @@ -151,6 +153,7 @@ private void WriteSourcePrologue(CompilationContext context, StringWriter output { context.WriteLine(output, "using PChecker;"); context.WriteLine(output, "using PChecker.Actors;"); + context.WriteLine(output, "using PChecker.Matcher;"); context.WriteLine(output, "using PChecker.Actors.Events;"); context.WriteLine(output, "using PChecker.Runtime;"); context.WriteLine(output, "using PChecker.Specifications;"); @@ -484,8 +487,8 @@ private void WriteEvent(CompilationContext context, StringWriter output, PEvent var payloadType = GetCSharpType(pEvent.PayloadType, true); context.WriteLine(output, $"internal partial class {declName} : PEvent"); context.WriteLine(output, "{"); - context.WriteLine(output, $"public {declName}() : base() {{}}"); - context.WriteLine(output, $"public {declName} ({payloadType} payload): base(payload)" + "{ }"); + context.WriteLine(output, $"public {declName}() : base(-1) {{}}"); + context.WriteLine(output, $"public {declName}({payloadType} payload, int loc): base(payload, loc)" + "{ }"); context.WriteLine(output, $"public override IPrtValue Clone() {{ return new {declName}();}}"); context.WriteLine(output, "}"); @@ -510,12 +513,12 @@ private void WriteMachine(CompilationContext context, StringWriter output, Machi var cTorType = GetCSharpType(machine.PayloadType, true); context.Write(output, "public class ConstructorEvent : PEvent"); context.Write(output, "{"); - context.Write(output, $"public ConstructorEvent({cTorType} val) : base(val) {{ }}"); + context.Write(output, $"public ConstructorEvent({cTorType} val, int loc) : base(val, loc) {{ }}"); context.WriteLine(output, "}"); context.WriteLine(output); context.WriteLine(output, - $"protected override Event GetConstructorEvent(IPrtValue value) {{ return new ConstructorEvent(({cTorType})value); }}"); + $"protected override Event GetConstructorEvent(IPrtValue value) {{ return new ConstructorEvent(({cTorType})value, {_sendEventIndex++}); }}"); // create the constructor to initialize the sends, creates and receives list WriteMachineConstructor(context, output, machine); @@ -706,7 +709,7 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func var staticKeyword = isStatic ? "static " : ""; var asyncKeyword = isAsync ? "async " : ""; - var returnType = GetCSharpType(signature.ReturnType); + var returnType = function.Role != FunctionRole.Scenario ? GetCSharpType(signature.ReturnType) : "int"; if (isAsync) { @@ -719,6 +722,10 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func { functionParameters = "Event currentMachine_dequeuedEvent"; } + else if (function.Role == FunctionRole.Scenario) + { + functionParameters = "List events"; + } else { functionParameters = string.Join( @@ -727,7 +734,7 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func $"{GetCSharpType(param.Type)} {context.Names.GetNameForDecl(param)}")); } - if (isStatic) // then we need to generate two versions of the function + if (isStatic && function.Role != FunctionRole.Scenario) // then we need to generate two versions of the function { // for machine var seperator = functionParameters == "" ? "" : ", "; @@ -783,14 +790,69 @@ private void WriteFunctionBody(CompilationContext context, StringWriter output, $"{GetCSharpType(type, true)} {context.Names.GetNameForDecl(local)} = {GetDefaultValue(type)};"); } - foreach (var bodyStatement in function.Body.Statements) + if (function.Role != FunctionRole.Scenario) + { + foreach (var bodyStatement in function.Body.Statements) + { + WriteStmt(context: context, output: output, function: function, stmt: bodyStatement); + } + } + else { - WriteStmt(context: context, output: output, function: function, stmt: bodyStatement); + WriteScenario(context, output, function); } + context.WriteLine(output, "}"); } + private void WriteScenario(CompilationContext context, StringWriter output, Function function) + { + int numOfStmt = function.Body.Statements.Count + 1; + context.WriteLine(output, $"int state = {numOfStmt};"); + var eventPredicates = string.Join(" or ", function.Signature.ParameterEvents.Select(it => it.Name)); + context.WriteLine(output, $"events = events.Where(it => it.Event is {eventPredicates}).ToList();"); + WriteConstraintsRecursive(context, output, function, 0, new HashSet(), 0); + context.WriteLine(output, "return state;"); + } + + private void WriteConstraintsRecursive(CompilationContext context, StringWriter output, Function function, int index, HashSet visitedVariables, int satisfiedConstraints) + { + if (index >= function.Signature.Parameters.Count) + { + context.WriteLine(output, "return 1;"); + return; + } + var param = function.Signature.Parameters[index]; + var e = function.Signature.ParameterEvents[index]; + visitedVariables.Add(param); + var start = index == 0 ? "0" : $"i{index - 1} + 1"; + var paramName = context.Names.GetNameForDecl(param); + context.WriteLine(output, $"for (var i{index} = {start} ; i{index} < events.Count; i{index} ++) " + "{"); + context.WriteLine(output, $"var {paramName}_obj = events[i{index}];"); + context.WriteLine(output, $"if ({paramName}_obj.Event is not {e.Name}) continue;"); + context.WriteLine(output, $"var {paramName} = ((PEvent) {paramName}_obj.Event).Payload;"); + + foreach (var bodyStatement in function.Body.Statements) + { + if (bodyStatement is ConstraintStmt stmt) + { + var variables = ConstraintVariableCollector.FindVariablesRecursive(stmt.Constraint); + if (variables.Contains(param) && visitedVariables.IsSupersetOf(variables)) + { + context.Write(output, $"if (!("); + WriteExpr(context, output, stmt.Constraint); + context.WriteLine(output, $")) continue;"); + satisfiedConstraints += 1; + context.WriteLine(output, $"state = Math.Min({function.Body.Statements.Count - satisfiedConstraints + 1}, state);"); + } + } + } + WriteConstraintsRecursive(context, output, function, index + 1, visitedVariables, satisfiedConstraints); + context.WriteLine(output, "}"); + } + + private void WriteStmt(CompilationContext context, StringWriter output, Function function, IPStmt stmt) { switch (stmt) @@ -1179,9 +1241,19 @@ private void WriteLValue(CompilationContext context, StringWriter output, IPExpr break; case NamedTupleAccessExpr namedTupleAccessExpr: - context.Write(output, "((PrtNamedTuple)"); - WriteExpr(context, output, namedTupleAccessExpr.SubExpr); - context.Write(output, $")[\"{namedTupleAccessExpr.FieldName}\"]"); + if (ExprVisitor.ReservedEventFeilds.ContainsValue(namedTupleAccessExpr.Entry)) + { + var type = GetCSharpType(namedTupleAccessExpr.Entry.Type); + context.Write(output, $"(({type}) ("); + WriteExpr(context, output, namedTupleAccessExpr.SubExpr); + context.Write(output, $"_obj).{namedTupleAccessExpr.FieldName})"); + } + else + { + context.Write(output, "((PrtNamedTuple)"); + WriteExpr(context, output, namedTupleAccessExpr.SubExpr); + context.Write(output, $")[\"{namedTupleAccessExpr.FieldName}\"]"); + } break; case SeqAccessExpr seqAccessExpr: @@ -1225,9 +1297,9 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p context.Write(output, $"({negate}PrtValues.SafeEquals("); if (PLanguageType.TypeIsOfKind(binOpExpr.Lhs.Type, TypeKind.Enum)) { - context.Write(output, "PrtValues.Box((long) "); + context.Write(output, "PrtValues.Box((long) ((PrtInt)"); WriteExpr(context, output, binOpExpr.Lhs); - context.Write(output, "),"); + context.Write(output, ")),"); } else { @@ -1237,9 +1309,9 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p if (PLanguageType.TypeIsOfKind(binOpExpr.Rhs.Type, TypeKind.Enum)) { - context.Write(output, "PrtValues.Box((long) "); + context.Write(output, "PrtValues.Box((long) ((PrtInt)"); WriteExpr(context, output, binOpExpr.Rhs); - context.Write(output, ")"); + context.Write(output, "))"); } else { @@ -1388,7 +1460,7 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p switch (eventName) { case "Halt": - context.Write(output, "new PHalt()"); + context.Write(output, "new PHalt(" + _sendEventIndex++ + ")"); break; case "DefaultEvent": @@ -1397,7 +1469,7 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p default: var payloadExpr = GetDefaultValue(eventRefExpr.Value.PayloadType); - context.Write(output, $"new {eventName}({payloadExpr})"); + context.Write(output, $"new {eventName}({payloadExpr}, {_sendEventIndex++})"); break; } diff --git a/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs b/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs index 943eba0df8..8e6992cf9b 100644 --- a/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs +++ b/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs @@ -567,6 +567,9 @@ private List SimplifyStatement(IPStmt statement) condCheck.Concat(SimplifyStatement(whileStmt.Body))); return new List { new WhileStmt(location, new BoolLiteralExpr(location, true), loopBody) }; + // We do not rewrite constraint statements for now. + case ConstraintStmt constraintStmt: + return new List() { constraintStmt }; default: throw new ArgumentOutOfRangeException(nameof(statement)); diff --git a/Src/PCompiler/CompilerCore/Parser/PLexer.g4 b/Src/PCompiler/CompilerCore/Parser/PLexer.g4 index 8f7511ac8d..e8fb715019 100644 --- a/Src/PCompiler/CompilerCore/Parser/PLexer.g4 +++ b/Src/PCompiler/CompilerCore/Parser/PLexer.g4 @@ -70,6 +70,7 @@ MODULE : 'module' ; IMPLEMENTATION : 'implementation' ; TEST : 'test' ; REFINES : 'refines' ; +SCENARIO : 'scenario' ; // module constructors COMPOSE : 'compose' ; diff --git a/Src/PCompiler/CompilerCore/Parser/PParser.g4 b/Src/PCompiler/CompilerCore/Parser/PParser.g4 index dc00fca475..3140b37edb 100644 --- a/Src/PCompiler/CompilerCore/Parser/PParser.g4 +++ b/Src/PCompiler/CompilerCore/Parser/PParser.g4 @@ -60,6 +60,7 @@ topDecl : typeDefDecl | namedModuleDecl | testDecl | implementationDecl + | scenarioDecl ; @@ -107,6 +108,13 @@ funDecl : FUN name=iden LPAREN funParamList? RPAREN (COLON type)? (CREATES inter | FUN name=iden LPAREN funParamList? RPAREN (COLON type)? functionBody # PFunDecl ; +scenarioDecl : SCENARIO scenarioName=iden LPAREN scenarioParamList RPAREN scenarioBody; + +scenarioParamList: scenarioParam (COMMA scenarioParam)*; +scenarioParam : name=iden COLON nonDefaultEvent; +scenarioBody : LBRACE expr (COMMA expr)* RBRACE; + + stateDecl : START? temperature=(HOT | COLD)? STATE name=iden LBRACE stateBodyItem* RBRACE ; stateBodyItem : ENTRY anonEventHandler # StateEntry diff --git a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs index c30a91299c..7ca2b40f49 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs @@ -15,7 +15,8 @@ public enum FunctionRole EventHandler = 1 << 4, ExitHandler = 1 << 5, ReceiveHandler = 1 << 6, - Foreign = 1 << 7 + Foreign = 1 << 7, + Scenario = 1 << 8, } public class Function : IPDecl, IHasScope @@ -32,7 +33,8 @@ sourceNode is PParser.AnonEventHandlerContext || sourceNode is PParser.NoParamAnonEventHandlerContext || sourceNode is PParser.ReceiveStmtContext || sourceNode is PParser.WhileStmtContext || - sourceNode is PParser.ForeachStmtContext); + sourceNode is PParser.ForeachStmtContext || + sourceNode is PParser.ScenarioDeclContext); Name = name; SourceLocation = sourceNode; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs index b711938af6..c3801faab7 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs @@ -7,6 +7,8 @@ namespace Plang.Compiler.TypeChecker.AST.Declarations public class FunctionSignature { public List Parameters { get; } = new List(); + // This is only used by scenarios. + public List ParameterEvents { get; } = new(); public IEnumerable ParameterTypes => Parameters.Select(ty => ty.Type); public PLanguageType ReturnType { get; set; } = PrimitiveType.Null; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs b/Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs new file mode 100644 index 0000000000..163e1a5953 --- /dev/null +++ b/Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs @@ -0,0 +1,15 @@ +using Antlr4.Runtime; + +namespace Plang.Compiler.TypeChecker.AST.Statements; + +public class ConstraintStmt : IPStmt +{ + public ConstraintStmt(ParserRuleContext sourceLocation, IPExpr constraint) + { + SourceLocation = sourceLocation; + Constraint = constraint; + } + + public IPExpr Constraint { get; } + public ParserRuleContext SourceLocation { get; } +} \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs b/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs index 050be18cca..aac694afb0 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs @@ -180,6 +180,13 @@ private static Scope BuildGlobalScope(ICompilerConfiguration config, PParser.Pro DeclarationVisitor.PopulateDeclarations(config.Handler, globalScope, programUnit, nodesToDeclarations); } + // Step 3: Assign param types for scenario events. We have do this after all events are initialized. + foreach (var function in globalScope.Functions) + { + ScenarioEventVisitor.PopulateEventTypes(function); + } + + return globalScope; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs b/Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs new file mode 100644 index 0000000000..647d9c1168 --- /dev/null +++ b/Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Plang.Compiler.TypeChecker.AST; +using Plang.Compiler.TypeChecker.AST.Declarations; +using Plang.Compiler.TypeChecker.AST.Expressions; + +namespace Plang.Compiler.TypeChecker; + +public class ConstraintVariableCollector +{ + + public static HashSet FindVariablesRecursive(IPExpr expr) + { + switch (expr) + { + case BinOpExpr binOp: + return FindVariablesRecursive(binOp.Lhs).Union(FindVariablesRecursive(binOp.Rhs)).ToHashSet(); + case NamedTupleAccessExpr namedTupleAccessExpr: + return FindVariablesRecursive(namedTupleAccessExpr.SubExpr); + case VariableAccessExpr variableAccessExpr: + return new HashSet() { variableAccessExpr.Variable }; + case EnumElemRefExpr: + case StringExpr: + case IntLiteralExpr: + break; + default: + throw new ArgumentOutOfRangeException(nameof(expr)); + } + + return new HashSet(); + } +} \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs b/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs index ea09002934..bddcbbf892 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs @@ -84,6 +84,7 @@ private void CheckStmt(IPStmt stmt) case RemoveStmt _: case ReturnStmt _: case SendStmt _: + case ConstraintStmt _: break; default: diff --git a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs index 77782e7308..c2bf47b518 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs @@ -163,6 +163,26 @@ public override object VisitStateDecl(PParser.StateDeclContext context) #endregion Machines + #region Scenario + + public override object VisitScenarioDecl(PParser.ScenarioDeclContext context) + { + var scenarioName = context.scenarioName.GetText(); + var scenario = CurrentScope.Put(scenarioName, context); + nodesToDeclarations.Put(context, scenario); + return VisitChildrenWithNewScope(scenario, context); + } + + public override object VisitScenarioParam(PParser.ScenarioParamContext context) + { + var symbolName = context.name.GetText(); + var decl = CurrentScope.Put(symbolName, context, VariableRole.Param); + nodesToDeclarations.Put(context, decl); + return null; + } + + #endregion + #region Functions public override object VisitPFunDecl(PParser.PFunDeclContext context) diff --git a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs index b726380da1..0c02c0a2a9 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using Antlr4.Runtime.Tree; using Plang.Compiler.TypeChecker.AST; using Plang.Compiler.TypeChecker.AST.Declarations; @@ -349,6 +350,29 @@ public override object VisitSpecMachineDecl(PParser.SpecMachineDeclContext conte return specMachine; } + public override object VisitScenarioDecl(PParser.ScenarioDeclContext context) + { + var scenario = (Function)nodesToDeclarations.Get(context); + var result = ((Variable, PEvent)[]) Visit(context.scenarioParamList()); + scenario.Signature.Parameters.AddRange(result.Select(it => it.Item1)); + scenario.Signature.ParameterEvents.AddRange(result.Select(it => it.Item2)); + scenario.Signature.ReturnType = PrimitiveType.Int; + scenario.Role = FunctionRole.Scenario; + return scenario; + } + + public override object VisitScenarioParamList(PParser.ScenarioParamListContext context) + { + return context.scenarioParam().Select(Visit).Cast<(Variable, PEvent)>().ToArray(); + } + + public override object VisitScenarioParam(PParser.ScenarioParamContext context) + { + var param = (Variable) nodesToDeclarations.Get(context); + var e = (PEvent) Visit(context.nonDefaultEvent()); + return (param, e); + } + public override object VisitMachineBody(PParser.MachineBodyContext context) { foreach (var machineEntryContext in context.machineEntry()) diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs index bd63b504db..6306c0659d 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs @@ -12,6 +12,18 @@ namespace Plang.Compiler.TypeChecker { public class ExprVisitor : PParserBaseVisitor { + public static readonly string EventFieldSender = "MSG_sender"; + public static readonly string EventFieldReceiver = "MSG_receiver"; + public static readonly string EventFieldState = "MSG_state"; + public static readonly string EventFieldIndex = "MSG_index"; + + public static Dictionary ReservedEventFeilds = new Dictionary() + { + { EventFieldSender, new NamedTupleEntry("Sender", -1, PrimitiveType.String) }, + { EventFieldReceiver, new NamedTupleEntry("Receiver", -1, PrimitiveType.String) }, + { EventFieldState, new NamedTupleEntry("State", -1, PrimitiveType.String) }, + { EventFieldIndex, new NamedTupleEntry("Index", -1, PrimitiveType.Int) }, + }; private readonly ITranslationErrorHandler handler; private readonly Function method; private readonly Scope table; @@ -46,12 +58,18 @@ public override IPExpr VisitParenExpr(PParser.ParenExprContext context) public override IPExpr VisitNamedTupleAccessExpr(PParser.NamedTupleAccessExprContext context) { var subExpr = Visit(context.expr()); + var fieldName = context.field.GetText(); + if (subExpr is VariableAccessExpr v && method.Role == FunctionRole.Scenario && method.Signature.Parameters.Contains(v.Variable)) + { + if (ReservedEventFeilds.TryGetValue(fieldName, out var reservedEntry)) + { + return new NamedTupleAccessExpr(context, subExpr, reservedEntry); + } + } if (!(subExpr.Type.Canonicalize() is NamedTupleType tuple)) { throw handler.TypeMismatch(subExpr, TypeKind.NamedTuple); } - - var fieldName = context.field.GetText(); if (!tuple.LookupEntry(fieldName, out var entry)) { throw handler.MissingNamedTupleEntry(context.field, tuple); @@ -605,7 +623,7 @@ public override IPExpr VisitNamedTupleBody(PParser.NamedTupleBodyContext context } names.Add(entryName); - entries[i] = new NamedTupleEntry { Name = entryName, FieldNo = i, Type = fields[i].Type }; + entries[i] = new NamedTupleEntry(name: entryName, fieldNo: i, type: fields[i].Type); } var type = new NamedTupleType(entries); diff --git a/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs index 2cb0b21eb3..b3572fb73f 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs @@ -1,6 +1,9 @@ +using System.Collections.Generic; using System.Diagnostics.Contracts; +using Plang.Compiler.TypeChecker.AST; using Plang.Compiler.TypeChecker.AST.Declarations; using Plang.Compiler.TypeChecker.AST.Statements; +using Plang.Compiler.TypeChecker.Types; namespace Plang.Compiler.TypeChecker { @@ -60,6 +63,30 @@ public override object VisitFunctionBody(PParser.FunctionBodyContext context) return null; } + public override object VisitScenarioDecl(PParser.ScenarioDeclContext context) + { + return Visit(context.scenarioBody()); + } + + public override object VisitScenarioBody(PParser.ScenarioBodyContext context) + { + var exprVisitor = new ExprVisitor(method, config.Handler); + // var compound = new CompoundStmt(context, new IPStmt[0]); + var stmts = new List(); + foreach (var exprContext in context.expr()) + { + var constraint = exprVisitor.Visit(exprContext); + if (!Equals(constraint.Type, PrimitiveType.Bool)) + { + throw config.Handler.TypeMismatch(exprContext, constraint.Type, PrimitiveType.Bool); + } + stmts.Add( new ConstraintStmt(exprContext, constraint)); + } + + method.Body = new CompoundStmt(context, stmts); + return null; + } + public override object VisitVarDecl(PParser.VarDeclContext context) { foreach (var varName in context.idenList()._names) diff --git a/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs b/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs index 16b1204a1a..35bd8a3be4 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs @@ -11,7 +11,7 @@ public static class FunctionValidator { public static void CheckAllPathsReturn(ITranslationErrorHandler handler, Function function) { - if (function.IsForeign) + if (function.IsForeign || function.Role == FunctionRole.Scenario) { return; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs new file mode 100644 index 0000000000..3cd4518397 --- /dev/null +++ b/Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs @@ -0,0 +1,20 @@ +using Antlr4.Runtime.Tree; +using Plang.Compiler.TypeChecker.AST; +using Plang.Compiler.TypeChecker.AST.Declarations; + +namespace Plang.Compiler.TypeChecker; + +public class ScenarioEventVisitor +{ + + public static void PopulateEventTypes(Function function) + { + if (function.Role != FunctionRole.Scenario) return; + for (var i = 0; i < function.Signature.Parameters.Count; i++) + { + var v = function.Signature.Parameters[i]; + var e = function.Signature.ParameterEvents[i]; + v.Type = e.PayloadType; + } + } +} \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs b/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs index fed805c40d..70051a4c03 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs @@ -516,6 +516,14 @@ public Interface Put(string name, PParser.InterfaceDeclContext tree) return machineInterface; } + public Function Put(string name, PParser.ScenarioDeclContext tree) + { + var scenario = new Function(name, tree); + CheckConflicts(scenario, Namespace(functions)); + functions.Add(name, scenario); + return scenario; + } + public Machine Put(string name, PParser.ImplMachineDeclContext tree) { var machine = new Machine(name, tree); diff --git a/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs b/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs index 39b22c3681..3fb843d8d9 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs @@ -121,7 +121,7 @@ public override PLanguageType VisitNamedTupleType(PParser.NamedTupleTypeContext } names.Add(fieldName); - fields[i] = new NamedTupleEntry { Name = fieldName, FieldNo = i, Type = Visit(field.type()) }; + fields[i] = new NamedTupleEntry(name: fieldName, fieldNo: i, type: Visit(field.type())); } var ret = new NamedTupleType(fields); diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs b/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs index f9223f79c4..89ad48ae17 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs @@ -2,6 +2,17 @@ namespace Plang.Compiler.TypeChecker.Types { public class NamedTupleEntry { + public NamedTupleEntry() + { + } + + public NamedTupleEntry(string name, int fieldNo, PLanguageType type) + { + Name = name; + FieldNo = fieldNo; + Type = type; + } + public string Name { get; set; } public int FieldNo { get; set; } public PLanguageType Type { get; set; } diff --git a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs index 19b045004c..f50d9eefa1 100644 --- a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs +++ b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs @@ -54,9 +54,22 @@ internal PCheckerOptions() var schedulingGroup = Parser.GetOrCreateGroup("scheduling", "Search prioritization options"); schedulingGroup.AddArgument("sch-random", null, "Choose the random scheduling strategy (this is the default)", typeof(bool)); + schedulingGroup.AddArgument("sch-feedback", null, "Choose the random scheduling strategy with feedback mutation", typeof(bool)); + schedulingGroup.AddArgument("sch-2stagefeedback", null, "Choose the random scheduling strategy with 2 stage feedback mutation", typeof(bool)); + + schedulingGroup.AddArgument("sch-feedbackpct", null, "Choose the PCT scheduling strategy with feedback mutation", typeof(uint)); + schedulingGroup.AddArgument("sch-feedbackpctcp", null, "Choose the PCT scheduling strategy with feedback mutation", typeof(uint)); + schedulingGroup.AddArgument("sch-feedbackpos", null, + "Choose the PCT scheduling strategy with feedback mutation", typeof(bool)); + schedulingGroup.AddArgument("sch-rff", null, "Choose the RFF scheduling strategy", typeof(bool)); + schedulingGroup.AddArgument("sch-2stagefeedbackpct", null, "Choose the PCT scheduling strategy with 2 stage feedback mutation", typeof(uint)); + schedulingGroup.AddArgument("sch-probabilistic", "sp", "Choose the probabilistic scheduling strategy with given probability for each scheduling decision where the probability is " + "specified as the integer N in the equation 0.5 to the power of N. So for N=1, the probability is 0.5, for N=2 the probability is 0.25, N=3 you get 0.125, etc.", typeof(uint)); schedulingGroup.AddArgument("sch-pct", null, "Choose the PCT scheduling strategy with given maximum number of priority switch points", typeof(uint)); + schedulingGroup.AddArgument("sch-pctcp", null, "Choose the PCT scheduling strategy with given maximum number of priority switch points", typeof(uint)); + schedulingGroup.AddArgument("sch-pos", null, + "Choose the PCT scheduling strategy with given maximum number of priority switch points", typeof(bool)); schedulingGroup.AddArgument("sch-fairpct", null, "Choose the fair PCT scheduling strategy with given maximum number of priority switch points", typeof(uint)); schedulingGroup.AddArgument("sch-rl", null, "Choose the reinforcement learning (RL) scheduling strategy", typeof(bool)).IsHidden = true; var schCoverage = schedulingGroup.AddArgument("sch-coverage", null, "Choose the scheduling strategy for coverage mode (options: learn, random, dfs, stateless). (default: learn)"); @@ -74,7 +87,13 @@ internal PCheckerOptions() advancedGroup.AddArgument("xml-trace", null, "Specify a filename for XML runtime log output to be written to", typeof(bool)); advancedGroup.AddArgument("psym-args", null, "Specify a concatenated list of additional PSym-specific arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("jvm-args", null, "Specify a concatenated list of PSym-specific JVM arguments to pass, each starting with a colon").IsHidden = true; - + advancedGroup.AddArgument("pattern", null, "The name of the pattern matcher generator", typeof(string)); + advancedGroup.AddArgument("no-partial-match", null, "For feedback strategy, do not save a schedule if the pattern is partially matched", typeof(bool)); + advancedGroup.AddArgument("discard-after", null, "For feedback strategy, discard saved generators after saving N inputs", typeof(int)); + advancedGroup.AddArgument("fixed-priority", null, "For feedback strategy, schedule generator mutations based on diversity", typeof(bool)); + advancedGroup.AddArgument("ignore-pattern", null, "For feedback strategy, ignore the pattern feedback", typeof(bool)); + advancedGroup.AddArgument("no-priority-based", null, "For feedback strategy, disable priority based sampling.", typeof(bool)); + advancedGroup.AddArgument("conflict-analysis", null, "Enable POS conflict analysis.", typeof(bool)); } /// @@ -227,11 +246,20 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c checkerConfiguration.RandomGeneratorSeed = (uint)option.Value; break; case "sch-random": + case "sch-rff": + case "sch-pos": + case "sch-feedbackpos": + case "sch-feedback": + case "sch-2stagefeedback": checkerConfiguration.SchedulingStrategy = option.LongName.Substring(4); break; case "sch-probabilistic": case "sch-pct": + case "sch-pctcp": case "sch-fairpct": + case "sch-feedbackpct": + case "sch-feedbackpctcp": + case "sch-2stagefeedbackpct": checkerConfiguration.SchedulingStrategy = option.LongName.Substring(4); checkerConfiguration.StrategyBound = (int)(uint)option.Value; break; @@ -311,6 +339,27 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c case "jvm-args": checkerConfiguration.JvmArgs = ((string)option.Value).Replace(':', ' '); break; + case "pattern": + checkerConfiguration.PatternSource = (string) option.Value; + break; + case "no-partial-match": + checkerConfiguration.SavePartialMatch = false; + break; + case "discard-after": + checkerConfiguration.DiscardAfter = (int) option.Value; + break; + case "fixed-priority": + checkerConfiguration.DiversityBasedPriority = false; + break; + case "ignore-pattern": + checkerConfiguration.IgnorePatternFeedback = true; + break; + case "no-priority-based": + checkerConfiguration.PriorityBasedSampling = false; + break; + case "conflict-analysis": + checkerConfiguration.EnableConflictAnalysis = true; + break; case "pproj": // do nothing, since already configured through UpdateConfigurationWithPProjectFile break; @@ -337,15 +386,23 @@ private static void SanitizeConfiguration(CheckerConfiguration checkerConfigurat if (checkerConfiguration.SchedulingStrategy != "portfolio" && checkerConfiguration.SchedulingStrategy != "random" && + checkerConfiguration.SchedulingStrategy != "pctcp" && + checkerConfiguration.SchedulingStrategy != "feedback" && + checkerConfiguration.SchedulingStrategy != "feedbackpct" && + checkerConfiguration.SchedulingStrategy != "feedbackpctcp" && + checkerConfiguration.SchedulingStrategy != "feedbackpos" && + checkerConfiguration.SchedulingStrategy != "2stagefeedback" && + checkerConfiguration.SchedulingStrategy != "2stagefeedbackpct" && checkerConfiguration.SchedulingStrategy != "pct" && + checkerConfiguration.SchedulingStrategy != "pos" && checkerConfiguration.SchedulingStrategy != "fairpct" && checkerConfiguration.SchedulingStrategy != "probabilistic" && checkerConfiguration.SchedulingStrategy != "rl" && checkerConfiguration.SchedulingStrategy != "replay" && checkerConfiguration.SchedulingStrategy != "learn" && checkerConfiguration.SchedulingStrategy != "dfs" && - checkerConfiguration.SchedulingStrategy != "stateless") - { + checkerConfiguration.SchedulingStrategy != "rff" && + checkerConfiguration.SchedulingStrategy != "stateless") { Error.CheckerReportAndExit("Please provide a scheduling strategy (see --sch* options)"); } @@ -378,7 +435,6 @@ private static void FindLocalPCompiledFile(CheckerConfiguration checkerConfigura enumerationOptions.RecurseSubdirectories = true; enumerationOptions.MaxRecursionDepth = 3; - var files = from file in Directory.GetFiles(checkerConfiguration.PCompiledPath, filePattern, enumerationOptions) let info = new FileInfo(file) diff --git a/Src/PRuntimes/PCSharpRuntime/PEvent.cs b/Src/PRuntimes/PCSharpRuntime/PEvent.cs index 78767414a5..a528315cb8 100644 --- a/Src/PRuntimes/PCSharpRuntime/PEvent.cs +++ b/Src/PRuntimes/PCSharpRuntime/PEvent.cs @@ -6,13 +6,15 @@ namespace Plang.CSharpRuntime { public class PEvent : Event, IPrtValue { - public PEvent() : base() + public PEvent(int loc) : base() { + Loc = loc; } - public PEvent(IPrtValue payload) : base() + public PEvent(IPrtValue payload, int loc) : base() { Payload = payload; + Loc = loc; } public IPrtValue Payload { get; } @@ -50,7 +52,7 @@ public override string ToString() public class PHalt : PEvent { - public PHalt(IPrtValue payload) : base(payload) + public PHalt(IPrtValue payload, int loc) : base(payload, loc) { } } diff --git a/Src/PRuntimes/PCSharpRuntime/PMachine.cs b/Src/PRuntimes/PCSharpRuntime/PMachine.cs index 3777ea5975..c8300ef401 100644 --- a/Src/PRuntimes/PCSharpRuntime/PMachine.cs +++ b/Src/PRuntimes/PCSharpRuntime/PMachine.cs @@ -84,7 +84,7 @@ public void TrySendEvent(PMachineValue target, Event ev, object payload = null) Assert(target.Permissions.Contains(ev.GetType().Name), $"Event {ev.GetType().Name} is not in the permissions set of the target machine"); var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); - ev = (Event)oneArgConstructor.Invoke(new[] { payload }); + ev = (Event)oneArgConstructor.Invoke(new[] { payload , ev.Loc}); AnnounceInternal(ev); SendEvent(target.Id, ev); @@ -94,7 +94,7 @@ public void TryRaiseEvent(Event ev, object payload = null) { Assert(ev != null, "Machine cannot raise a null event"); var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); - ev = (Event)oneArgConstructor.Invoke(new[] { payload }); + ev = (Event)oneArgConstructor.Invoke(new[] { payload, ev.Loc }); RaiseEvent(ev); throw new PNonStandardReturnException { ReturnKind = NonStandardReturn.Raise }; } @@ -182,7 +182,7 @@ public void Announce(Event ev, object payload = null) } var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); - var @event = (Event)oneArgConstructor.Invoke(new[] { payload }); + var @event = (Event)oneArgConstructor.Invoke(new[] { payload, ev.Loc }); var pText = payload == null ? "" : $" with payload {((IPrtValue)payload).ToEscapedString()}"; Logger.WriteLine($" '{Id}' announced event '{ev.GetType().Name}'{pText}."); @@ -247,7 +247,7 @@ public object ToDict() public class InitializeParametersEvent : PEvent { - public InitializeParametersEvent(InitializeParameters payload) : base(payload) + public InitializeParametersEvent(InitializeParameters payload) : base(payload, 0) { } } diff --git a/Tutorial/2_TwoPhaseCommit/timeline.txt b/Tutorial/2_TwoPhaseCommit/timeline.txt new file mode 100644 index 0000000000..e69de29bb2 From c6f74dcc777cf9943ba02872bcec737d05de017b Mon Sep 17 00:00:00 2001 From: Ankush Desai Date: Mon, 15 Apr 2024 09:10:23 -0700 Subject: [PATCH 02/21] Improving the code with some cleanup. (#719) * Disable trace logging to save disk space. * Update feedback algorithm. * fix feedback strategy. * refactor. * Remove experiment features. --------- Co-authored-by: Ao Li --- .../CheckerCore/Actors/ActorRuntime.cs | 2 + .../CheckerCore/Actors/Events/Event.cs | 4 + .../Actors/Logging/ActorRuntimeLogBase.cs | 124 ++++++ .../CheckerCore/CheckerConfiguration.cs | 23 +- .../Coverage/ActivityCoverageReporter.cs | 0 .../Coverage/ActorRuntimeLogEventCoverage.cs | 0 .../Coverage/ActorRuntimeLogGraphBuilder.cs | 0 .../Feedback => }/Coverage/CoverageInfo.cs | 0 Src/PChecker/CheckerCore/Pattern/EventObj.cs | 22 -- .../Pattern/EventPatternObserver.cs | 188 --------- Src/PChecker/CheckerCore/Pattern/IMatcher.cs | 10 - .../CheckerCore/Specifications/Monitor.cs | 1 + .../SystematicTesting/ControlledRuntime.cs | 16 - .../{Coverage => }/ConflictOpMonitor.cs | 19 +- .../Feedback/Coverage/AbstractSchedule.cs | 70 ---- .../Coverage/AbstractScheduleObserver.cs | 348 ----------------- .../Feedback/Coverage/EventPatternObserver.cs | 51 +++ .../Feedback/Coverage/ISendEventMonitor.cs | 7 - .../Strategies/Feedback/Coverage/Operation.cs | 9 + .../Feedback/Coverage/TimelineObserver.cs | 137 +------ .../Feedback/FeedbackGuidedStrategy.cs | 64 ++- .../Generator/Mutator/PctcpScheduleMutator.cs | 19 - .../Generator/PctcpScheduleGenerator.cs | 68 ---- .../Generator/RandomScheduleGenerator.cs | 4 +- .../Feedback/IFeedbackGuidedStrategy.cs | 2 +- .../Strategies/Feedback/MaxHeap.cs | 88 ----- .../Feedback/TwoStageFeedbackStrategy.cs | 36 -- .../Strategies/Probabilistic/PCTCP/Chain.cs | 33 -- .../Probabilistic/PCTCP/OperationWithId.cs | 3 - .../Probabilistic/PCTCP/VectorClockWrapper.cs | 8 - .../Probabilistic/PCTCPScheduler.cs | 366 ------------------ .../Strategies/Probabilistic/POSStrategy.cs | 48 --- .../PrioritizedSchedulingStrategy.cs | 10 +- .../Probabilistic/QLearningStrategy.cs | 6 +- .../Strategies/Probabilistic/RFFScheduler.cs | 174 --------- .../SystematicTesting/TestingEngine.cs | 189 ++++----- .../Backend/CSharp/CSharpCodeGenerator.cs | 9 +- .../PCommandLine/Options/PCheckerOptions.cs | 28 +- Src/PRuntimes/PCSharpRuntime/PMachine.cs | 2 + 39 files changed, 334 insertions(+), 1854 deletions(-) create mode 100644 Src/PChecker/CheckerCore/Actors/Logging/ActorRuntimeLogBase.cs rename Src/PChecker/CheckerCore/{SystematicTesting/Strategies/Feedback => }/Coverage/ActivityCoverageReporter.cs (100%) rename Src/PChecker/CheckerCore/{SystematicTesting/Strategies/Feedback => }/Coverage/ActorRuntimeLogEventCoverage.cs (100%) rename Src/PChecker/CheckerCore/{SystematicTesting/Strategies/Feedback => }/Coverage/ActorRuntimeLogGraphBuilder.cs (100%) rename Src/PChecker/CheckerCore/{SystematicTesting/Strategies/Feedback => }/Coverage/CoverageInfo.cs (100%) delete mode 100644 Src/PChecker/CheckerCore/Pattern/EventObj.cs delete mode 100644 Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs delete mode 100644 Src/PChecker/CheckerCore/Pattern/IMatcher.cs rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/{Coverage => }/ConflictOpMonitor.cs (84%) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs diff --git a/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs b/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs index 1959d9c76e..690f06c901 100644 --- a/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs +++ b/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs @@ -264,6 +264,8 @@ private Actor CreateActor(ActorId id, Type type, string name, Actor creator, Gui /// internal virtual void SendEvent(ActorId targetId, Event e, Actor sender, Guid opGroupId) { + e.Sender = sender.ToString(); + e.Receiver = targetId.ToString(); var enqueueStatus = EnqueueEvent(targetId, e, sender, opGroupId, out var target); if (enqueueStatus is EnqueueStatus.EventHandlerNotRunning) { diff --git a/Src/PChecker/CheckerCore/Actors/Events/Event.cs b/Src/PChecker/CheckerCore/Actors/Events/Event.cs index 132cc5c69b..ca9fe6b4d5 100644 --- a/Src/PChecker/CheckerCore/Actors/Events/Event.cs +++ b/Src/PChecker/CheckerCore/Actors/Events/Event.cs @@ -12,5 +12,9 @@ namespace PChecker.Actors.Events public abstract class Event { public int Loc { get; set; } + public string? Sender; + public string? Receiver; + public string? State; + public int Index; } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Actors/Logging/ActorRuntimeLogBase.cs b/Src/PChecker/CheckerCore/Actors/Logging/ActorRuntimeLogBase.cs new file mode 100644 index 0000000000..203a73464d --- /dev/null +++ b/Src/PChecker/CheckerCore/Actors/Logging/ActorRuntimeLogBase.cs @@ -0,0 +1,124 @@ +using System; +using PChecker.Actors.Events; + +namespace PChecker.Actors.Logging; + +internal abstract class ActorRuntimeLogBase : IActorRuntimeLog +{ + public virtual void OnCreateActor(ActorId id, string creatorName, string creatorType) + { + } + + public virtual void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) + { + } + + public virtual void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, + string actionName) + { + } + + public virtual void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, + Event e, + Guid opGroupId, bool isTargetHalted) + { + } + + public virtual void OnRaiseEvent(ActorId id, string stateName, Event e) + { + } + + public virtual void OnEnqueueEvent(ActorId id, Event e) + { + } + + public virtual void OnDequeueEvent(ActorId id, string stateName, Event e) + { + } + + public virtual void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked) + { + } + + public virtual void OnWaitEvent(ActorId id, string stateName, Type eventType) + { + } + + public virtual void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes) + { + } + + public virtual void OnStateTransition(ActorId id, string stateName, bool isEntry) + { + } + + public virtual void OnGotoState(ActorId id, string currentStateName, string newStateName) + { + } + + public virtual void OnDefaultEventHandler(ActorId id, string stateName) + { + } + + public virtual void OnHalt(ActorId id, int inboxSize) + { + } + + public virtual void OnHandleRaisedEvent(ActorId id, string stateName, Event e) + { + } + + public virtual void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) + { + } + + public virtual void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex) + { + } + + public virtual void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex) + { + } + + public virtual void OnCreateMonitor(string monitorType) + { + } + + public virtual void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) + { + } + + public virtual void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, + string senderType, + string senderStateName, Event e) + { + } + + public virtual void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) + { + } + + public virtual void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) + { + } + + public virtual void OnMonitorError(string monitorType, string stateName, bool? isInHotState) + { + } + + public virtual void OnRandom(object result, string callerName, string callerType) + { + } + + public virtual void OnAssertionFailure(string error) + { + } + + public virtual void OnStrategyDescription(string strategyName, string description) + { + } + + public virtual void OnCompleted() + { + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index e27f18bb4c..57706a2b4f 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -292,12 +292,6 @@ public int MaxSchedulingSteps [DataMember] public string JvmArgs; - /// - /// For feedback strategy, save input if the pattern are partially matched. - /// - [DataMember] - public bool SavePartialMatch; - /// /// For feedback strategy, discard saved generators if the size of the buffer is greater than N. /// @@ -305,23 +299,11 @@ public int MaxSchedulingSteps public int DiscardAfter; /// - /// For feedback strategy, schedule generator mutations based on diversity. + /// For QL strategy, schedule generator mutations based on diversity. /// [DataMember] public bool DiversityBasedPriority; - /// - /// For feedback strategy, ignore the pattern feedback. - /// - [DataMember] - public bool IgnorePatternFeedback; - - /// - /// For feedback strategy, use priority based sampling. - /// - [DataMember] - public bool PriorityBasedSampling; - /// /// Enable conflict analysis for scheduling optimization. /// @@ -373,11 +355,8 @@ protected CheckerConfiguration() EnableColoredConsoleOutput = false; DisableEnvironmentExit = true; - SavePartialMatch = true; DiscardAfter = 100; DiversityBasedPriority = true; - IgnorePatternFeedback = false; - PriorityBasedSampling = true; EnableConflictAnalysis = false; PSymArgs = ""; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActivityCoverageReporter.cs b/Src/PChecker/CheckerCore/Coverage/ActivityCoverageReporter.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActivityCoverageReporter.cs rename to Src/PChecker/CheckerCore/Coverage/ActivityCoverageReporter.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogEventCoverage.cs b/Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogEventCoverage.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogEventCoverage.cs rename to Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogEventCoverage.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogGraphBuilder.cs b/Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogGraphBuilder.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ActorRuntimeLogGraphBuilder.cs rename to Src/PChecker/CheckerCore/Coverage/ActorRuntimeLogGraphBuilder.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/CoverageInfo.cs b/Src/PChecker/CheckerCore/Coverage/CoverageInfo.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/CoverageInfo.cs rename to Src/PChecker/CheckerCore/Coverage/CoverageInfo.cs diff --git a/Src/PChecker/CheckerCore/Pattern/EventObj.cs b/Src/PChecker/CheckerCore/Pattern/EventObj.cs deleted file mode 100644 index 2accf8d86b..0000000000 --- a/Src/PChecker/CheckerCore/Pattern/EventObj.cs +++ /dev/null @@ -1,22 +0,0 @@ -using PChecker.Actors.Events; -using PChecker.Specifications.Monitors; - -namespace PChecker.Matcher; - -public class EventObj -{ - public Event Event; - public string? Sender; - public string? Receiver; - public string State; - public int Index; - - public EventObj(Event e, string? sender, string? receiver, string state, int index) - { - Event = e; - Sender = sender; - Receiver = receiver; - State = state; - Index = index; - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs b/Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs deleted file mode 100644 index c0dfdaa26f..0000000000 --- a/Src/PChecker/CheckerCore/Pattern/EventPatternObserver.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using PChecker.Actors; -using PChecker.Actors.Events; -using PChecker.Actors.Logging; -using PChecker.Matcher; - -namespace PChecker.Feedback; - -internal class EventPatternObserver : IActorRuntimeLog -{ - private MethodInfo _matcher; - private Dictionary _senderMap = new(); - private List _events = new(); - public EventPatternObserver(MethodInfo matcher) - { - _matcher = matcher; - } - - public void OnCreateActor(ActorId id, string creatorName, string creatorType) - { - - } - - public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) - { - } - - public void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName) - { - - } - - public void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, Event e, - Guid opGroupId, bool isTargetHalted) - { - _senderMap[e] = senderName; - } - - public void OnRaiseEvent(ActorId id, string stateName, Event e) - { - - } - - public void OnEnqueueEvent(ActorId id, Event e) - { - - } - - public void OnDequeueEvent(ActorId id, string stateName, Event e) - { - _events.Add(new EventObj(e, _senderMap.GetValueOrDefault(e), id.Name, stateName, _events.Count)); - } - - - public void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked) - { - - } - - public void OnWaitEvent(ActorId id, string stateName, Type eventType) - { - - } - - public void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes) - { - - } - - public void OnStateTransition(ActorId id, string stateName, bool isEntry) - { - - } - - public void OnGotoState(ActorId id, string currentStateName, string newStateName) - { - } - - public void OnPushState(ActorId id, string currentStateName, string newStateName) - { - - } - - public void OnPopState(ActorId id, string currentStateName, string restoredStateName) - { - - } - - public void OnDefaultEventHandler(ActorId id, string stateName) - { - - } - - public void OnHalt(ActorId id, int inboxSize) - { - - } - - public void OnHandleRaisedEvent(ActorId id, string stateName, Event e) - { - - } - - public void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) - { - - } - - public void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex) - { - - } - - public void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex) - { - - } - - public void OnCreateMonitor(string monitorType) - { - - } - - public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) - { - - } - - public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, - string senderStateName, Event e) - { - _events.Add(new EventObj(e, senderName, null, stateName, _events.Count)); - } - - public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) - { - - } - - public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) - { - - } - - public void OnMonitorError(string monitorType, string stateName, bool? isInHotState) - { - - } - - public void OnRandom(object result, string callerName, string callerType) - { - - } - - public void OnAssertionFailure(string error) - { - - } - - public void OnStrategyDescription(string strategyName, string description) - { - - } - - public void OnCompleted() - { - } - - public virtual int ShouldSave() - { - return (int) _matcher.Invoke(null, new [] { _events }); - } - - public virtual bool IsMatched() - { - int result = (int) _matcher.Invoke(null, new [] { _events }); - return result == 1; - } - - - public void Reset() - { - _events.Clear(); - _senderMap.Clear(); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Pattern/IMatcher.cs b/Src/PChecker/CheckerCore/Pattern/IMatcher.cs deleted file mode 100644 index 2faa030882..0000000000 --- a/Src/PChecker/CheckerCore/Pattern/IMatcher.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using PChecker.Matcher; - -namespace PChecker.Feedback; - -public interface IMatcher -{ - - public int IsMatched(List events); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Specifications/Monitor.cs b/Src/PChecker/CheckerCore/Specifications/Monitor.cs index acf62c8dc9..5314899c18 100644 --- a/Src/PChecker/CheckerCore/Specifications/Monitor.cs +++ b/Src/PChecker/CheckerCore/Specifications/Monitor.cs @@ -275,6 +275,7 @@ protected void Assert(bool predicate, string s, params object[] args) /// internal void MonitorEvent(Event e, string senderName, string senderType, string senderState) { + e.Sender = senderName; Runtime.LogWriter.LogMonitorProcessEvent(GetType().FullName, CurrentStateName, senderName, senderType, senderState, e); HandleEvent(e); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs index ac91696d2f..2ed17adb40 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs @@ -66,14 +66,6 @@ internal sealed class ControlledRuntime : ActorRuntime /// internal readonly int? RootTaskId; - /// - /// The observer that extracts the timeline information of the scheduling. - /// - internal readonly TimelineObserver TimelineObserver = new(); - - public List SendEventMonitors = new(); - - /// /// Returns the current hashed state of the monitors. /// @@ -156,7 +148,6 @@ internal ControlledRuntime(CheckerConfiguration checkerConfiguration, ISchedulin // Update the current asynchronous control flow with this runtime instance, // allowing future retrieval in the same asynchronous call stack. AssignAsyncControlFlowRuntime(this); - RegisterLog(TimelineObserver); } /// @@ -471,10 +462,6 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Guid Scheduler.ScheduledOperation.LastEvent = e; Scheduler.ScheduledOperation.LastSentReceiver = targetId.ToString(); - foreach (var monitor in SendEventMonitors) { - monitor.OnSendEvent(sender.Id, e.Loc, targetId, LogWriter.JsonLogger.VcGenerator); - } - Scheduler.ScheduleNextEnabledOperation(AsyncOperationType.Send); ResetProgramCounter(sender as StateMachine); @@ -495,9 +482,6 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Guid return EnqueueStatus.Dropped; } - foreach (var monitor in SendEventMonitors) { - monitor.OnSendEventDone(sender.Id, e.Loc, targetId, LogWriter.JsonLogger.VcGenerator); - } var enqueueStatus = EnqueueEvent(target, e, sender, opGroupId); if (enqueueStatus == EnqueueStatus.Dropped) { diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs similarity index 84% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs index 561d7f0e86..00cef43310 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ConflictOpMonitor.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs @@ -1,15 +1,18 @@ +using System; using System.Collections.Generic; using System.Linq; using PChecker.Actors; +using PChecker.Actors.Events; using PChecker.Actors.Logging; using PChecker.SystematicTesting.Operations; namespace PChecker.Feedback; -public class ConflictOpMonitor: ISendEventMonitor +internal class ConflictOpMonitor: ActorRuntimeLogBase { + public VectorClockGenerator VectorClockGenerator; // This dictionary stores all operations received by a machine. // Each operation is labeled with ActorId, source location, and its corresponding @@ -18,12 +21,12 @@ public class ConflictOpMonitor: ISendEventMonitor private Dictionary> conflictOps = new(); - - public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) + public override void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, Event e, + Guid opGroupId, bool isTargetHalted) { - var receiverKey = receiver.ToString(); - var senderKey = sender.ToString(); - var currentOp = new Operation(senderKey, receiverKey, loc); + var receiverKey = e.Receiver; + var senderKey = e.Sender; + var currentOp = new Operation(senderKey, receiverKey, e.Loc); if (!incomingOps.ContainsKey(receiverKey)) { incomingOps.Add(receiverKey, new HashSet<(Operation, Dictionary)>()); @@ -32,7 +35,7 @@ public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGe - if (currentVc.ContextVcMap.TryGetValue(sender.Name, out var vectorClock)) + if (VectorClockGenerator.ContextVcMap.TryGetValue(senderKey, out var vectorClock)) { foreach (var op in opsSet) @@ -52,8 +55,6 @@ public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGe } } - public void OnSendEventDone(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) {} - internal bool IsRacing(AsyncOperation op1, AsyncOperation op2) { if (op1.Type != AsyncOperationType.Send || op2.Type != AsyncOperationType.Send) { diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs deleted file mode 100644 index 3d0b14d9d8..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractSchedule.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using PChecker.Actors; -using PChecker.Random; - - -enum State { - PosReachR, - PosExctdW, - PosNoInfo, - PosSat, - NegExctdW, - NegReachW, - NegOtherW, - NegNoInfo, - NegUnsat, -} - -public record Constraint(Operation op1, Operation op2, bool positive) -{ - public override string ToString() - { - return $"({op1}, {op2}, {positive})"; - } -} -public record AbstractSchedule(HashSet constraints) { - - internal AbstractSchedule Mutate(List allConstraints, IRandomValueGenerator random) - { - List constraints = new(this.constraints); - - int op = random.Next(4); - switch (op) { - case 0: { - - int index = random.Next(allConstraints.Count); - constraints.Add(allConstraints[index]); - break; - } - case 1: { - if (constraints.Count > 1) { - int index = random.Next(constraints.Count); - constraints.RemoveAt(index); - index = random.Next(allConstraints.Count); - constraints.Add(allConstraints[index]); - } - break; - } - case 2: { - if (constraints.Count > 1) { - int index = random.Next(constraints.Count); - constraints.RemoveAt(index); - } - break; - } - case 3: { - if (constraints.Count > 1) { - int index = random.Next(constraints.Count); - var c = constraints[index]; - constraints.RemoveAt(index); - constraints.Add(new Constraint(c.op1, c.op2, !c.positive)); - } - break; - } - } - return new(constraints.ToHashSet()); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs deleted file mode 100644 index 36723486f4..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/AbstractScheduleObserver.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using PChecker.Actors; -using PChecker.Actors.Logging; -using PChecker.SystematicTesting.Operations; - -internal class AbstractScheduleObserver : ISendEventMonitor -{ - AbstractSchedule abstractSchedule; - Dictionary> opQueue = new(); - Dictionary constraintState = new(); - public Dictionary> avoidSendTo = new(); - public Dictionary> avoidSchedule = new(); - public Dictionary> lookingForSchedule = new(); - public Dictionary> lookingForSendTo = new(); - public Dictionary> relevantConstraintsByReceiver = new(); - public Dictionary> relevantConstraintsByOp = new(); - public bool newConstraint = false; - - public Dictionary visitedConstraints = new(); - public Dictionary allVisitedConstraints = new(); - - public void OnNewAbstractSchedule(AbstractSchedule schedule) - { - abstractSchedule = schedule; - newConstraint = false; - - // Reset everything; - opQueue.Clear(); - avoidSendTo.Clear(); - avoidSchedule.Clear(); - constraintState.Clear(); - lookingForSchedule.Clear(); - lookingForSendTo.Clear(); - relevantConstraintsByOp.Clear(); - relevantConstraintsByReceiver.Clear(); - visitedConstraints.Clear(); - - foreach (var constraint in abstractSchedule.constraints) { - if (constraint.positive) { - constraintState[constraint] = State.PosNoInfo; - } else { - constraintState[constraint] = State.NegNoInfo; - } - - if (!avoidSendTo.ContainsKey(constraint.op1.Receiver)) { - avoidSendTo[constraint.op1.Receiver] = new(); - } - - if (!lookingForSendTo.ContainsKey(constraint.op1.Receiver)) { - lookingForSendTo[constraint.op1.Receiver] = new(); - } - - if (!relevantConstraintsByReceiver.ContainsKey(constraint.op1.Receiver)) { - relevantConstraintsByReceiver[constraint.op1.Receiver] = new(); - } - - if (!relevantConstraintsByOp.ContainsKey(constraint.op1)) { - relevantConstraintsByOp[constraint.op1] = new(); - } - - if (!relevantConstraintsByOp.ContainsKey(constraint.op2)) { - relevantConstraintsByOp[constraint.op2] = new(); - } - - if (!avoidSchedule.ContainsKey(constraint.op1)) { - avoidSchedule[constraint.op1] = new(); - } - - if (!avoidSchedule.ContainsKey(constraint.op2)) { - avoidSchedule[constraint.op2] = new(); - } - - if (!lookingForSchedule.ContainsKey(constraint.op1)) { - lookingForSchedule[constraint.op1] = new(); - } - - if (!lookingForSchedule.ContainsKey(constraint.op2)) { - lookingForSchedule[constraint.op2] = new(); - } - - - relevantConstraintsByOp[constraint.op1].Add(constraint); - relevantConstraintsByOp[constraint.op2].Add(constraint); - } - } - - - public HashSet GetRelevantConstraints(Operation op) - { - var constraints = new HashSet(); - if (relevantConstraintsByReceiver.ContainsKey(op.Receiver)) { - constraints.UnionWith(relevantConstraintsByReceiver[op.Receiver]); - } - if (relevantConstraintsByOp.ContainsKey(op)) { - constraints.UnionWith(relevantConstraintsByOp[op]); - } - return constraints; - } - - public void OnExecute(Operation op) - { - foreach (var constraint in GetRelevantConstraints(op)) - { - var newState = constraintState[constraint]; - if (constraint.positive) - { - if (constraint.op1 == op) - { - newState = State.PosExctdW; - } - else if (constraintState[constraint] == State.PosReachR && constraint.op2 == op) - { - newState = State.PosNoInfo; - } - else if (constraintState[constraint] == State.PosExctdW && constraint.op2 == op) - { - newState = State.PosSat; - relevantConstraintsByOp[constraint.op1].Remove(constraint); - relevantConstraintsByOp[constraint.op2].Remove(constraint); - } - else if (constraintState[constraint] == State.PosExctdW && avoidSendTo[op.Receiver].Contains(constraint)) - { - newState = State.PosNoInfo; - } - } - else - { - if (constraint.op1 == op) - { - newState = State.NegExctdW; - } - else if (constraintState[constraint] == State.NegExctdW && constraint.op2 != op) - { - newState = State.NegOtherW; - } - else if (constraintState[constraint] == State.NegOtherW && constraint.op2 == op) - { - newState = State.NegOtherW; - } - else if (constraintState[constraint] == State.NegExctdW && constraint.op2 == op) - { - newState = State.NegUnsat; - relevantConstraintsByOp[constraint.op1].Remove(constraint); - relevantConstraintsByOp[constraint.op2].Remove(constraint); - } - - } - if (newState != constraintState[constraint]) - { - CleanState(constraint); - constraintState[constraint] = newState; - UpdateLookFor(constraint); - } - } - - } - - public void OnNewOp(Operation op) - { - foreach (var constraint in GetRelevantConstraints(op)) - { - var newState = constraintState[constraint]; - if (constraint.positive) - { - if (constraintState[constraint] != State.PosExctdW && constraint.op2 == op) - { - newState = State.PosReachR; - } - } - else - { - if (constraint.op1 == op) - { - newState = State.NegReachW; - } - } - if (newState != constraintState[constraint]) - { - CleanState(constraint); - constraintState[constraint] = newState; - UpdateLookFor(constraint); - } - } - } - - public void CleanState(Constraint constraint) - { - switch (constraintState[constraint]) - { - case State.PosReachR: - avoidSchedule[constraint.op2].Remove(constraint); - lookingForSchedule[constraint.op1].Remove(constraint); - break; - case State.PosExctdW: - lookingForSchedule[constraint.op2].Remove(constraint); - avoidSendTo[constraint.op1.Receiver].Remove(constraint); - relevantConstraintsByReceiver[constraint.op1.Receiver].Remove(constraint); - break; - case State.NegExctdW: - avoidSchedule[constraint.op2].Remove(constraint); - lookingForSendTo[constraint.op1.Receiver].Remove(constraint); - relevantConstraintsByReceiver[constraint.op1.Receiver].Remove(constraint); - break; - case State.NegReachW: - avoidSchedule[constraint.op1].Remove(constraint); - break; - case State.NegOtherW: - lookingForSchedule[constraint.op2].Remove(constraint); - break; - } - } - - public void UpdateLookFor(Constraint constraint) - { - switch (constraintState[constraint]) - { - case State.PosReachR: - avoidSchedule[constraint.op2].Add(constraint); - lookingForSchedule[constraint.op1].Add(constraint); - break; - case State.PosExctdW: - lookingForSchedule[constraint.op2].Add(constraint); - avoidSendTo[constraint.op1.Receiver].Add(constraint); - relevantConstraintsByReceiver[constraint.op1.Receiver].Add(constraint); - break; - case State.NegExctdW: - avoidSchedule[constraint.op2].Add(constraint); - lookingForSendTo[constraint.op1.Receiver].Add(constraint); - relevantConstraintsByReceiver[constraint.op1.Receiver].Add(constraint); - break; - case State.NegReachW: - avoidSchedule[constraint.op1].Add(constraint); - break; - case State.NegOtherW: - lookingForSchedule[constraint.op2].Add(constraint); - break; - } - } - - public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) - { - var receiverName = receiver.ToString(); - var senderName = sender.ToString(); - - OnNewOp(new Operation(senderName, receiverName, loc)); - } - - public void OnSendEventDone(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc) - { - var receiverName = receiver.ToString(); - var senderName = sender.ToString(); - - if (!opQueue.ContainsKey(receiverName)) - { - opQueue[receiverName] = new(); - } - var queue = opQueue[receiverName]; - - queue.Add(new Operation(senderName, receiverName, loc)); - OnExecute(new Operation(senderName, receiverName, loc)); - - if (queue.Count > 1) { - var op1 = queue[queue.Count - 2]; - var op2 = queue[queue.Count - 1]; - - var c = new Constraint(op1, op2, true); - - if (!visitedConstraints.ContainsKey(c)) { - visitedConstraints[c] = 0; - } - visitedConstraints[c] += 1; - } - } - - public int GetTraceHash() { - if (visitedConstraints.Count == 0) { - return "".GetHashCode(); - } - string s = visitedConstraints.ToList().Select(it => $"<{it.Key}, {it.Value}>").OrderBy(it => it).Aggregate((current, next) => current + "," + next); - return s.GetHashCode(); - } - - public bool CheckNoveltyAndUpdate() - { - bool isNovel = false; - foreach (var constraint in visitedConstraints) { - if (!allVisitedConstraints.ContainsKey(constraint.Key)) - { - allVisitedConstraints[constraint.Key] = 0; - } - if (constraint.Value > allVisitedConstraints[constraint.Key]) - { - allVisitedConstraints[constraint.Key] = constraint.Value; - isNovel = true; - } - } - return isNovel; - } - - public bool CheckAbstractTimelineSatisfied() - { - return constraintState.All(it => { - if (it.Key.positive) { - return it.Value == State.PosSat; - } else { - return it.Value != State.NegUnsat; - } - }); - } - - public bool ShouldAvoid(AsyncOperation op) - { - if (op.Type == AsyncOperationType.Send) - { - var operation = new Operation(op.Name, op.LastSentReceiver, op.LastEvent!.Loc); - if (avoidSchedule.ContainsKey(operation)) - { - return true; - } - if (avoidSendTo.ContainsKey(operation.Receiver) - && avoidSendTo[operation.Receiver].Any(it => it.op2 != operation)) - { - return true; - } - } - return false; - } - - public bool ShouldTake(AsyncOperation op) - { - if (op.Type == AsyncOperationType.Send) - { - var operation = new Operation(op.Name, op.LastSentReceiver, op.LastEvent!.Loc); - if (lookingForSchedule.ContainsKey(operation)) - { - return true; - } - if (lookingForSendTo.ContainsKey(operation.Receiver) && - lookingForSendTo[operation.Receiver].Any(it => it.op2 == operation)) - { - return true; - } - return false; - } - return true; - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs new file mode 100644 index 0000000000..61b74b57ea --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using PChecker.Actors; +using PChecker.Actors.Events; +using PChecker.Actors.Logging; + +namespace PChecker.Feedback; + +internal class EventPatternObserver : ActorRuntimeLogBase +{ + private MethodInfo _matcher; + private List _events = new(); + + public EventPatternObserver(MethodInfo matcher) + { + _matcher = matcher; + } + + public override void OnDequeueEvent(ActorId id, string stateName, Event e) + { + e.Index = _events.Count; + e.State = stateName; + _events.Add(e); + } + + public override void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, + string senderStateName, Event e) + { + e.Index = _events.Count; + e.State = stateName; + _events.Add(e); + } + + public virtual int ShouldSave() + { + return (int) _matcher.Invoke(null, new [] { _events }); + } + + public virtual bool IsMatched() + { + int result = (int) _matcher.Invoke(null, new [] { _events }); + return result == 1; + } + + + public void Reset() + { + _events.Clear(); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs index 394ddb3e9e..28014d7d24 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs @@ -1,13 +1,6 @@ using PChecker.Actors; using PChecker.Actors.Logging; -public record Operation(string Sender, string Receiver, int Loc) { - public override string ToString() - { - return $"<{Sender}, {Receiver}, {Loc}>"; - } - -} public interface ISendEventMonitor { public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs new file mode 100644 index 0000000000..4af176c918 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs @@ -0,0 +1,9 @@ +namespace PChecker.Feedback; + +public record Operation(string Sender, string Receiver, int Loc) { + public override string ToString() + { + return $"<{Sender}, {Receiver}, {Loc}>"; + } + +} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs index 0c0759e3ca..b4503e52a2 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs @@ -7,7 +7,7 @@ namespace PChecker.Feedback; -public class TimelineObserver: IActorRuntimeLog +internal class TimelineObserver: ActorRuntimeLogBase { private HashSet<(string, string, string)> _timelines = new(); @@ -28,36 +28,7 @@ static TimelineObserver() } } - public void OnCreateActor(ActorId id, string creatorName, string creatorType) - { - - } - - public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) - { - } - - public void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName) - { - - } - - public void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, Event e, - Guid opGroupId, bool isTargetHalted) - { - } - - public void OnRaiseEvent(ActorId id, string stateName, Event e) - { - - } - - public void OnEnqueueEvent(ActorId id, Event e) - { - - } - - public void OnDequeueEvent(ActorId id, string stateName, Event e) + public override void OnDequeueEvent(ActorId id, string stateName, Event e) { string actor = id.Type; @@ -110,108 +81,4 @@ public List GetTimelineMinhash() } return minHash; } - - public void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked) - { - - } - - public void OnWaitEvent(ActorId id, string stateName, Type eventType) - { - - } - - public void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes) - { - - } - - public void OnStateTransition(ActorId id, string stateName, bool isEntry) - { - - } - - public void OnGotoState(ActorId id, string currentStateName, string newStateName) - { - } - - public void OnDefaultEventHandler(ActorId id, string stateName) - { - - } - - public void OnHalt(ActorId id, int inboxSize) - { - - } - - public void OnHandleRaisedEvent(ActorId id, string stateName, Event e) - { - - } - - public void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) - { - - } - - public void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex) - { - - } - - public void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex) - { - - } - - public void OnCreateMonitor(string monitorType) - { - - } - - public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) - { - - } - - public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, - string senderStateName, Event e) - { - - } - - public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) - { - - } - - public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) - { - - } - - public void OnMonitorError(string monitorType, string stateName, bool? isInHotState) - { - - } - - public void OnRandom(object result, string callerName, string callerType) - { - - } - - public void OnAssertionFailure(string error) - { - - } - - public void OnStrategyDescription(string strategyName, string description) - { - - } - - public void OnCompleted() - { - } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs index 7d3f4a82f7..358bfe3c9f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs @@ -18,7 +18,7 @@ public record StrategyGenerator(TInput InputGenerator, TSchedule ScheduleGenerat public record GeneratorRecord(int Priority, StrategyGenerator Generator, List MinHash); - protected StrategyGenerator Generator; + internal StrategyGenerator Generator; private readonly int _maxScheduledSteps; @@ -28,14 +28,11 @@ public record GeneratorRecord(int Priority, StrategyGenerator Generator, List _savedGenerators = new (); private int _pendingMutations = 0; + private bool _shouldExploreNew = false; private HashSet _visitedGenerators = new HashSet(); private GeneratorRecord? _currentParent = null; - private readonly bool _savePartialMatch; - private readonly bool _diversityBasedPriority; - private readonly bool _ignorePatternFeedback; private readonly int _discardAfter; - private readonly bool _priorityBasedSampling; private System.Random _rnd = new System.Random(); @@ -54,11 +51,7 @@ public FeedbackGuidedStrategy(CheckerConfiguration checkerConfiguration, TInput _maxScheduledSteps = checkerConfiguration.MaxFairSchedulingSteps; } Generator = new StrategyGenerator(input, schedule); - _savePartialMatch = checkerConfiguration.SavePartialMatch; - _diversityBasedPriority = checkerConfiguration.DiversityBasedPriority; _discardAfter = checkerConfiguration.DiscardAfter; - _ignorePatternFeedback = checkerConfiguration.IgnorePatternFeedback; - _priorityBasedSampling = checkerConfiguration.PriorityBasedSampling; } /// @@ -136,12 +129,7 @@ private int ComputeDiversity(int timeline, List hash) return 0; } - if (!_priorityBasedSampling) - { - return 20; - } - - if (_savedGenerators.Count == 0 || !_diversityBasedPriority) + if (_savedGenerators.Count == 0) { return 20; } @@ -169,11 +157,10 @@ private int ComputeDiversity(int timeline, List hash) /// /// This method observes the results of previous run and prepare for the next run. /// - /// The ControlledRuntime of previous run. - public virtual void ObserveRunningResults(EventPatternObserver patternObserver, ControlledRuntime runtime) + public virtual void ObserveRunningResults(EventPatternObserver patternObserver, TimelineObserver timelineObserver) { - var timelineHash = runtime.TimelineObserver.GetTimelineHash(); - var timelineMinhash = runtime.TimelineObserver.GetTimelineMinhash(); + var timelineHash = timelineObserver.GetTimelineHash(); + var timelineMinhash = timelineObserver.GetTimelineMinhash(); int diversityScore = ComputeDiversity(timelineHash, timelineMinhash); @@ -183,18 +170,15 @@ public virtual void ObserveRunningResults(EventPatternObserver patternObserver, } int priority = 0; - if (patternObserver == null || _ignorePatternFeedback || !_priorityBasedSampling) + if (patternObserver == null) { priority = diversityScore; } else { int coverageResult = patternObserver.ShouldSave(); - if (coverageResult == 1 || _savePartialMatch) - { - double coverageScore = 1.0 / coverageResult; - priority = (int)(diversityScore * coverageScore); - } + double coverageScore = 1.0 / coverageResult; + priority = (int)(diversityScore * coverageScore); } if (priority > 0) @@ -221,7 +205,7 @@ public virtual void ObserveRunningResults(EventPatternObserver patternObserver, _savedGenerators.Insert(index, record); } - if (_priorityBasedSampling && _savedGenerators.Count > _discardAfter) + if (_savedGenerators.Count > _discardAfter) { var last = _savedGenerators.Last(); _visitedGenerators.Remove(last); @@ -245,44 +229,44 @@ private void PrepareNextInput() } else { - if (!_priorityBasedSampling && _pendingMutations == 0) - { - _currentParent = _savedGenerators[_rnd.Next(_savedGenerators.Count)]; - _pendingMutations = 50; - } - - if (_currentParent == null) + if (_currentParent == null && !_shouldExploreNew) { _currentParent = _savedGenerators.First(); _visitedGenerators.Add(_currentParent); - _pendingMutations = _currentParent.Priority; _pendingMutations = 50; } if (_pendingMutations == 0) { + _shouldExploreNew = false; bool found = false; foreach (var generator in _savedGenerators) { if (_visitedGenerators.Contains(generator)) continue; _currentParent = generator; _visitedGenerators.Add(generator); - _pendingMutations = generator.Priority; _pendingMutations = 50; found = true; } if (!found) { - _visitedGenerators.Clear(); - _currentParent = _savedGenerators.First(); - _visitedGenerators.Add(_currentParent); - _pendingMutations = _currentParent.Priority; + if (_rnd.NextDouble() < 0.5) + { + _visitedGenerators.Clear(); + _currentParent = _savedGenerators.First(); + _visitedGenerators.Add(_currentParent); + } + else + { + _shouldExploreNew = true; + _currentParent = null; + } _pendingMutations = 50; } } - Generator = MutateGenerator(_currentParent.Generator); + Generator = _shouldExploreNew ? NewGenerator() : MutateGenerator(_currentParent.Generator); _pendingMutations -= 1; } } diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs deleted file mode 100644 index b057aeac5a..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PctcpScheduleMutator.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace PChecker.Generator.Mutator; - -internal class PctcpScheduleMutator: IMutator -{ - private int _meanMutationCount = 5; - private int _meanMutationSize = 5; - private System.Random _random = new(); - public PctcpScheduleGenerator Mutate(PctcpScheduleGenerator prev) - { - return new PctcpScheduleGenerator(prev.Random, - Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), - Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random), - prev.MaxPrioritySwitchPoints, - prev.ScheduleLength, - prev.VcWrapper - ); - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs deleted file mode 100644 index 34ffe087d0..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PctcpScheduleGenerator.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using PChecker.Generator.Mutator; -using PChecker.Generator.Object; -using PChecker.SystematicTesting.Operations; -using PChecker.SystematicTesting.Strategies.Probabilistic; -using PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; - -namespace PChecker.Generator; - -internal class PctcpScheduleGenerator: PCTCPScheduler, IScheduleGenerator -{ - - public System.Random Random; - public RandomChoices PriorityChoices; - public RandomChoices SwitchPointChoices; - - public PctcpScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? - switchPointChoices, int numSwitchPoints, int maxScheduleLength, VectorClockWrapper wrapper): - base(numSwitchPoints, maxScheduleLength, - new ParametricProvider( - priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), - switchPointChoices != null ? new RandomChoices(switchPointChoices) : new - RandomChoices(random)), wrapper) - { - Random = random; - var provider = (ParametricProvider) Provider; - PriorityChoices = provider.PriorityChoices; - SwitchPointChoices = provider.SwitchPointChoices; - } - public PctcpScheduleGenerator(CheckerConfiguration checkerConfiguration, VectorClockWrapper wrapper): - this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, - null, checkerConfiguration.StrategyBound, 0, wrapper) - { - } - - public PctcpScheduleGenerator Mutate() - { - return new PctcpScheduleMutator().Mutate(this); - } - - public PctcpScheduleGenerator New() - { - return new PctcpScheduleGenerator(Random, null, null, MaxPrioritySwitchPoints, ScheduleLength, VcWrapper); - } - - public PctcpScheduleGenerator Copy() - { - return new PctcpScheduleGenerator(Random, PriorityChoices, SwitchPointChoices, MaxPrioritySwitchPoints, - ScheduleLength, VcWrapper); - } - - public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) - { - if (GetNextOperation(current, enabledOperations, out var next)) { - return next; - } else { - return null; - } - } - - - public void PrepareForNextInput() - { - PrepareForNextIteration(); - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs index 98f64b77a8..49d378c87c 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using PChecker.Generator.Mutator; using PChecker.Generator.Object; using PChecker.SystematicTesting.Operations; @@ -23,8 +24,9 @@ public RandomScheduleGenerator(CheckerConfiguration checkerConfiguration): { } - public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) + public AsyncOperation? NextRandomOperation(List ops, AsyncOperation current) { + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); if (enabledOperations.Count == 0) { return null; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs index 84f3eec289..f74b8b8b35 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs @@ -6,7 +6,7 @@ namespace PChecker.SystematicTesting.Strategies.Feedback; internal interface IFeedbackGuidedStrategy: ISchedulingStrategy { - public void ObserveRunningResults(EventPatternObserver patternObserver, ControlledRuntime runtime); + public void ObserveRunningResults(EventPatternObserver patternObserver, TimelineObserver timelineObserver); public int TotalSavedInputs(); public void DumpStats(TextWriter writer); } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs deleted file mode 100644 index b04f504de4..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/MaxHeap.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PChecker.SystematicTesting.Strategies.Feedback; - -public class MaxHeap -{ - public readonly List Elements = new(); - private IComparer _comparer; - - public MaxHeap(IComparer cmp) - { - _comparer = cmp; - } - - private int GetLeftChildIndex(int elementIndex) => 2 * elementIndex + 1; - private int GetRightChildIndex(int elementIndex) => 2 * elementIndex + 2; - private int GetParentIndex(int elementIndex) => (elementIndex - 1) / 2; - - private bool HasLeftChild(int elementIndex) => GetLeftChildIndex(elementIndex) < Elements.Count; - private bool HasRightChild(int elementIndex) => GetRightChildIndex(elementIndex) < Elements.Count; - private bool IsRoot(int elementIndex) => elementIndex == 0; - - private TValue GetLeftChild(int elementIndex) => Elements[GetLeftChildIndex(elementIndex)]; - private TValue GetRightChild(int elementIndex) => Elements[GetRightChildIndex(elementIndex)]; - private TValue GetParent(int elementIndex) => Elements[GetParentIndex(elementIndex)]; - - private void Swap(int firstIndex, int secondIndex) - { - (Elements[firstIndex], Elements[secondIndex]) = (Elements[secondIndex], Elements[firstIndex]); - } - - public TValue Peek() - { - return Elements[0]; - } - - public TValue Pop() - { - var result = Elements[0]; - Elements[0] = Elements[Elements.Count - 1]; - Elements.RemoveAt(Elements.Count - 1); - - ReCalculateDown(); - - return result; - } - - public void Add(TValue element) - { - Elements.Add(element); - ReCalculateUp(); - } - - private void ReCalculateDown() - { - int index = 0; - while (HasLeftChild(index)) - { - var biggerIndex = GetLeftChildIndex(index); - if (HasRightChild(index) && - _comparer.Compare(GetRightChild(index), GetLeftChild(index)) > 0) - { - biggerIndex = GetRightChildIndex(index); - } - - if (_comparer.Compare(Elements[biggerIndex], Elements[index]) < 0) - { - break; - } - - Swap(biggerIndex, index); - index = biggerIndex; - } - } - - private void ReCalculateUp() - { - var index = Elements.Count - 1; - while (!IsRoot(index) && _comparer.Compare(Elements[index], GetParent(index)) > 0) - { - var parentIndex = GetParentIndex(index); - Swap(parentIndex, index); - index = parentIndex; - } - } - -} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs deleted file mode 100644 index 3fdc1485ca..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/TwoStageFeedbackStrategy.cs +++ /dev/null @@ -1,36 +0,0 @@ -using PChecker.Generator; -using PChecker.Feedback; - -namespace PChecker.SystematicTesting.Strategies.Feedback; - - -internal class TwoStageFeedbackStrategy : FeedbackGuidedStrategy - where TInput: IInputGenerator - where TSchedule: IScheduleGenerator -{ - - private int _numScheduleMutationWithoutNewSaved = 0; - - // This number should be less than `FeedbackGuidedStrategy._maxMutationsWithoutNewSaved` - private readonly int _maxScheduleMutationsWithoutNewSaved = 25; - public TwoStageFeedbackStrategy(CheckerConfiguration checkerConfiguration, TInput input, TSchedule schedule) : base(checkerConfiguration, input, schedule) - { - } - - protected override StrategyGenerator MutateGenerator(StrategyGenerator prev) - { - if (_numScheduleMutationWithoutNewSaved > _maxScheduleMutationsWithoutNewSaved) - { - _numScheduleMutationWithoutNewSaved = 0; - return new StrategyGenerator( - Generator.InputGenerator.Mutate(), - // do not mutate schedule to save time? - Generator.ScheduleGenerator.Copy() - ); - } - return new StrategyGenerator( - Generator.InputGenerator.Copy(), - Generator.ScheduleGenerator.Mutate() - ); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs deleted file mode 100644 index add66d209d..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/Chain.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; - -public class Chain -{ - public List Ops = new(); - public int CurrentIndex = 0; - - public OperationWithId CurrentOp() { - if (CurrentIndex < Ops.Count) - { - return Ops[CurrentIndex]; - } - return null; - } - - public List SliceSuccessors(int op) - { - var index = Ops.FindIndex(it => it.Id == op); - return Ops.GetRange(index, Ops.Count - index - 1); - } - - public void AppendAll(List ops) - { - Ops.AddRange(ops); - } - - public void RemoveAll(List ops) - { - Ops.RemoveAll(it => ops.Contains(it)); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs deleted file mode 100644 index 343f83979c..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/OperationWithId.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; - -public record OperationWithId(string Sender, string Receiver, int Loc, int Id); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs deleted file mode 100644 index 46982c4d69..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCP/VectorClockWrapper.cs +++ /dev/null @@ -1,8 +0,0 @@ -using PChecker.Actors.Logging; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; - -public class VectorClockWrapper -{ - public VectorClockGenerator CurrentVC; -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs deleted file mode 100644 index dd6c0fd540..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTCPScheduler.cs +++ /dev/null @@ -1,366 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PChecker.Actors.Logging; -using PChecker.IO.Debugging; -using PChecker.SystematicTesting.Operations; -using PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic; - - -internal class PCTCPScheduler: PrioritizedScheduler -{ - public readonly PriorizationProvider Provider; - - private int ScheduledSteps; - public readonly int MaxPrioritySwitchPoints; - public int ScheduleLength; - private int _nextPriorityChangePoint; - private int _numSwitchPointsLeft; - private int _nextOperationId = 0; - private Dictionary _chainedOperations = new(); - private List _chains = new(); - private Dictionary> _vectorClockMap = new(); - private Dictionary> _predMap = new(); - private Dictionary _operationMap = new(); - private Dictionary _chainMap = new(); - private Dictionary _nextOperationMap = new(); - internal VectorClockWrapper VcWrapper; - - public PCTCPScheduler(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider, - VectorClockWrapper vcWrapper) - { - Provider = provider; - ScheduledSteps = 0; - ScheduleLength = scheduleLength; - MaxPrioritySwitchPoints = maxPrioritySwitchPoints; - _numSwitchPointsLeft = maxPrioritySwitchPoints; - VcWrapper = vcWrapper; - - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); - } - - public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) - { - next = null; - var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - if (enabledOperations.Count == 0) - { - if (_nextPriorityChangePoint == ScheduledSteps) - { - MovePriorityChangePointForward(); - } - return false; - } - - var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); - if (next is null) - { - next = highestEnabledOp; - } - - ScheduledSteps++; - return true; - } - - private void OnNewOperation(AsyncOperation operation) - { - Dictionary vc; - if (VcWrapper.CurrentVC.ContextVcMap.ContainsKey(operation.Name)) - { - vc = VcWrapper.CurrentVC - .ContextVcMap[operation.Name] - .ToDictionary(entry => entry.Key, entry => entry.Value); - } - else - { - vc = new(); - } - - OperationWithId op; - if (operation.Type == AsyncOperationType.Send) - { - op = new OperationWithId(operation.Name, operation.LastSentReceiver, operation - .LastEvent!.Loc, _nextOperationId++); - } - else - { - op = new OperationWithId(operation.Name, "", 0, _nextOperationId++); - } - - _vectorClockMap[op.Id] = vc; - _chainedOperations[operation.Name] = op.Id; - _operationMap[op.Id] = op; - _predMap[op.Id] = new(); - - for (int i = 0; i < op.Id - 1; i++) - { - if (IsLT(_vectorClockMap[i], _vectorClockMap[op.Id])) - { - _predMap[op.Id].Add(i); - } - } - - if (!PlaceInChains(op)) - { - Chain newChain = new(); - newChain.Ops.Add(op); - _chainMap[op.Id] = newChain; - if (_chains.Count == 0) - { - _chains.Add(newChain); - } - else - { - var index = Provider.AssignPriority(_chains.Count); - _chains.Insert(index, newChain); - } - } - } - - private bool PlaceInChains(OperationWithId op) - { - // var currentQ = _chains. - for (int i = 0; i < _chains.Count; i++) - { - var chain = _chains[i]; - if (chain.Ops.Count > 0) - { - var tail = chain.Ops.Last(); - if (IsLT(_vectorClockMap[tail.Id], _vectorClockMap[op.Id])) - { - chain.Ops.Add(op); - _nextOperationMap[tail.Id] = op.Id; - _chainMap[op.Id] = chain; - return true; - } - } - } - return false; - } - - bool IsLT(Dictionary vc1, Dictionary vc2) - { - bool hasLess = false; - foreach (var key in vc1.Keys.Union(vc2.Keys)) - { - var op1 = vc1.GetValueOrDefault(key, 0); - var op2 = vc2.GetValueOrDefault(key, 0); - if (op1 >= op2) - { - return false; - } - - if (op1 < op2) - { - hasLess = true; - } - } - return hasLess; - } - - private void MovePriorityChangePointForward() - { - _nextPriorityChangePoint += 1; - Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); - } - private OperationWithId GetHighestPriorityEnabledOperation(IEnumerable choices) - { - OperationWithId highestPriorityOp = null; - int currentPriority = Int32.MaxValue; - int currentChainIndex = Int32.MaxValue; - foreach (var op in choices) - { - var id = _chainedOperations[op.Name]; - var chain = _chainMap[id]; - var priotiy = _chains.IndexOf(chain); - if (priotiy < currentPriority) - { - highestPriorityOp = _operationMap[id]; - currentPriority = priotiy; - currentChainIndex = chain.Ops.IndexOf(highestPriorityOp); - } - - if (priotiy == currentPriority) - { - var index = chain.Ops.IndexOf(_operationMap[id]); - if (index < currentChainIndex) - { - highestPriorityOp = _operationMap[id]; - currentChainIndex = index; - } - } - } - return highestPriorityOp; - } - - private (int, int, Dictionary) FindReducingSequence() - { - var queue = new Queue(); - foreach (var chain in _chains) - { - if (chain.Ops.Count > 0) - { - queue.Enqueue(chain.Ops.First().Id); - } - } - - var pairs = new Dictionary(); - - while (queue.Count > 0) - { - var opId = queue.Dequeue(); - foreach (var chain in _chains) - { - if (chain == _chainMap[opId]) continue; - if (chain.Ops.Count <= 0) continue; - if (IsLT(_vectorClockMap[chain.Ops.Last().Id], _vectorClockMap[opId])) - { - return (chain.Ops.Last().Id, opId, pairs); - } - } - var temp = _predMap[opId].Where(it => _nextOperationMap.ContainsKey(it) - && !pairs.ContainsKey(_nextOperationMap[it])) - .ToList(); - foreach (var predOp in temp) - { - queue.Enqueue(_nextOperationMap[predOp]); - pairs[_nextOperationMap[predOp]] = (predOp, opId); - } - - } - return (-1, -1, pairs); - } - - private void ReduceChains() - { - var (pred, op, pairs) = FindReducingSequence(); - - if (pred == -1) return; - - do - { - var predChain = _chainMap[pred]; - var opChain = _chainMap[op]; - var ids = opChain.SliceSuccessors(op); - predChain.Ops.AddRange(ids); - opChain.Ops.RemoveAll(it => ids.Contains(it)); - foreach (var id in ids) - { - _chainMap[id.Id] = predChain; - } - - _nextOperationMap[pred] = op; - - if (opChain.Ops.Count > 0) - { - _nextOperationMap.Remove(opChain.Ops.Last().Id); - } - - if (!pairs.ContainsKey(op)) - { - _chains.Remove(opChain); - break; - } - pred = pairs[op].Item1; - op = pairs[op].Item2; - } while (true); - - } - - /// - /// Returns the prioritized operation. - /// - private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) - { - bool newOpAdded = false; - foreach (var op in ops.Where(op => !_chainedOperations.ContainsKey(op.Name))) - { - OnNewOperation(op); - newOpAdded = true; - } - - if (newOpAdded) - { - ReduceChains(); - } - - - var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); - if (_nextPriorityChangePoint == ScheduledSteps) - { - if (ops.Count == 1) - { - MovePriorityChangePointForward(); - } - else - { - var chain = _chainMap[prioritizedSchedulable.Id]; - _chains.Remove(chain); - _chains.Add(chain); - - _numSwitchPointsLeft -= 1; - // Update the next priority change point. - if (_numSwitchPointsLeft > 0) - { - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()) + ScheduledSteps; - } - } - } - - AsyncOperation scheduledOperation = null; - if (prioritizedSchedulable != null) - { - scheduledOperation = ops.First(it => it.Name == prioritizedSchedulable.Sender); - _chainedOperations.Remove(scheduledOperation.Name); - Debug.WriteLine(" scheduled operation: " + scheduledOperation.Name); - } - - return scheduledOperation; - } - - public void Reset() - { - ScheduleLength = 0; - ScheduledSteps = 0; - _chainedOperations.Clear(); - _vectorClockMap.Clear(); - _chains.Clear(); - _nextOperationMap.Clear(); - _nextOperationId = 0; - } - - /// - public virtual bool PrepareForNextIteration() - { - ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); - ScheduledSteps = 0; - _nextOperationId = 0; - _numSwitchPointsLeft = MaxPrioritySwitchPoints; - _chainedOperations.Clear(); - _vectorClockMap.Clear(); - _chains.Clear(); - _nextOperationMap.Clear(); - _chainMap.Clear(); - - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); - return true; - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs deleted file mode 100644 index 6968e1c6ee..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSStrategy.cs +++ /dev/null @@ -1,48 +0,0 @@ -using PChecker.Feedback; -using PChecker.Random; -using PChecker.SystematicTesting.Operations; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic; - -internal class POSStrategy: POSScheduler, ISchedulingStrategy -{ - /// - /// Random value generator. - /// - private readonly IRandomValueGenerator RandomValueGenerator; - - public POSStrategy(int maxSteps, ConflictOpMonitor? monitor, IRandomValueGenerator random) - : base(new RandomPriorizationProvider(random), monitor) - { - } - - public bool GetNextBooleanChoice(AsyncOperation current, int maxValue, out bool next) - { - throw new System.NotImplementedException(); - } - - public bool GetNextIntegerChoice(AsyncOperation current, int maxValue, out int next) - { - throw new System.NotImplementedException(); - } - - public int GetScheduledSteps() - { - throw new System.NotImplementedException(); - } - - public bool HasReachedMaxSchedulingSteps() - { - throw new System.NotImplementedException(); - } - - public bool IsFair() - { - throw new System.NotImplementedException(); - } - - public string GetDescription() - { - throw new System.NotImplementedException(); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs index 72660d7f97..6f50c24677 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs @@ -33,7 +33,7 @@ internal class PrioritizedSchedulingStrategy: ISchedulingStrategy private int _scheduledSteps; - private PrioritizedScheduler _scheduler; + internal readonly PrioritizedScheduler Scheduler; /// /// Initializes a new instance of the class. @@ -42,14 +42,14 @@ public PrioritizedSchedulingStrategy(int maxSteps, IRandomValueGenerator random, { RandomValueGenerator = random; MaxScheduledSteps = maxSteps; - _scheduler = scheduler; + Scheduler = scheduler; } /// public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) { _scheduledSteps++; - return _scheduler.GetNextOperation(current, ops, out next); + return Scheduler.GetNextOperation(current, ops, out next); } /// @@ -100,7 +100,7 @@ public string GetDescription() public bool PrepareForNextIteration() { _scheduledSteps = 0; - return _scheduler.PrepareForNextIteration(); + return Scheduler.PrepareForNextIteration(); } @@ -108,7 +108,7 @@ public bool PrepareForNextIteration() { public void Reset() { _scheduledSteps = 0; - _scheduler.Reset(); + Scheduler.Reset(); } } } diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs index 4b23127301..64ed1674c0 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs @@ -396,10 +396,10 @@ private int ComputeDiversity(int timeline, List hash) return (hash.Count - maxSim) * 10 + 20; } - public void ObserveRunningResults(EventPatternObserver patternObserver, ControlledRuntime runtime) + public void ObserveRunningResults(EventPatternObserver patternObserver, TimelineObserver timelineObserver) { - var timelineHash = runtime.TimelineObserver.GetTimelineHash(); - var timelineMinhash = runtime.TimelineObserver.GetTimelineMinhash(); + var timelineHash = timelineObserver.GetTimelineHash(); + var timelineMinhash = timelineObserver.GetTimelineMinhash(); int priority = 1; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs deleted file mode 100644 index 11d27c2e26..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RFFScheduler.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PChecker.Feedback; -using PChecker.Random; -using PChecker.SystematicTesting.Operations; -using PChecker.SystematicTesting.Strategies.Probabilistic; - -internal class ScheduleRecord -{ - public int NumVisit {get; set; } - public int FuzzLevel {get; set; } - public int Priority {get; set;} - public AbstractSchedule Schedule; - - public ScheduleRecord(int numVisit, int fuzzLevel, AbstractSchedule schedule) { - NumVisit = numVisit; - FuzzLevel = fuzzLevel; - Schedule = schedule; - } - -} -internal class RFFScheduler: PrioritizedScheduler -{ - // public List<(AbstractSchedule, )> savedSchedules = new(); - public Dictionary TraceRecords = new(); - public int SavedTraces = 0; - public int TotalExec = 1; - int Skip = 0; - int Adj = 1; - int SchedNonDets = 0; - int parentIndex = 0; - int count = 0; - internal AbstractSchedule currentSchedule; - internal AbstractScheduleObserver observer; - internal List savedSchedules = new(); - public IRandomValueGenerator random; - private POSScheduler _posScheduler; - - public RFFScheduler(IRandomValueGenerator random, ConflictOpMonitor monitor, AbstractScheduleObserver observer) - { - this.observer = observer; - this.random = random; - currentSchedule = new AbstractSchedule(new HashSet()); - observer.OnNewAbstractSchedule(currentSchedule); - _posScheduler = new POSScheduler(new RandomPriorizationProvider(random), monitor); - } - - public bool PrepareForNextIteration() - { - _posScheduler.PrepareForNextIteration(); - // We should always check novelty to update global states. - if (observer.CheckNoveltyAndUpdate() || observer.CheckAbstractTimelineSatisfied()) - { - int traceHash = observer.GetTraceHash(); - - if (!TraceRecords.ContainsKey(traceHash)) - { - TraceRecords[traceHash] = new ScheduleRecord(0, 0, currentSchedule); - savedSchedules.Add(TraceRecords[traceHash]); - } - var record = TraceRecords[traceHash]; - record.NumVisit += 1; - - int u = TotalExec / (SavedTraces + Adj); - - int factor; - if (record.NumVisit <= u) - { - if (record.FuzzLevel < 31) - { - record.FuzzLevel += 1; - } - - factor = (1 << record.FuzzLevel) / u; - Skip = 0; - } - else - { - factor = 0; - Skip += 1; - if (Skip >= SchedNonDets) - { - Skip = 0; - Adj += 1; - } - } - int PerfScore = Math.Min(factor * 1, 50); - record.Priority = PerfScore; - SavedTraces += 1; - } - TotalExec += 1; - - - if (parentIndex == -1 && savedSchedules.Count > 0) - { - parentIndex = 0; - } - - if (parentIndex == -1) - { - currentSchedule = currentSchedule.Mutate(observer.allVisitedConstraints.Keys.ToList(), random); - return true; - } - - if (count < savedSchedules[parentIndex].Priority) - { - currentSchedule = savedSchedules[parentIndex].Schedule.Mutate(observer.allVisitedConstraints.Keys.ToList(), random); - count += 1; - } else { - parentIndex += 1; - if (parentIndex >= savedSchedules.Count) { - parentIndex = 0; - } - count = 0; - } - - observer.OnNewAbstractSchedule(currentSchedule); - return true; - } - - public void Reset() - { - _posScheduler.Reset(); - } - - public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) - { - var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - if (enabledOperations.Count == 0) - { - next = null; - return false; - } - if (enabledOperations.Count == 1) - { - next = enabledOperations[0]; - return true; - } - - var highPrioOps = new List(); - var normalPrioOps = new List(); - var lowPrioOps = new List(); - foreach (var op in enabledOperations) - { - var avoid = observer.ShouldAvoid(op); - var take = observer.ShouldTake(op); - - if (avoid && !take) - { - lowPrioOps.Add(op); - } - else if (!avoid && take) - { - highPrioOps.Add(op); - } - else - { - normalPrioOps.Add(op); - } - - } - - if (highPrioOps.Count > 0) - { - return _posScheduler.GetNextOperation(current, highPrioOps, out next); - } - if (normalPrioOps.Count > 0) - { - return _posScheduler.GetNextOperation(current, normalPrioOps, out next); - } - return _posScheduler.GetNextOperation(current, lowPrioOps, out next); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index ed668c03ea..876873d3e3 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -20,6 +20,7 @@ using PChecker.Coverage; using PChecker.Feedback; using PChecker.Generator; +using PChecker.Generator.Mutator; using PChecker.IO; using PChecker.IO.Debugging; using PChecker.IO.Logging; @@ -29,7 +30,6 @@ using PChecker.SystematicTesting.Strategies.Exhaustive; using PChecker.SystematicTesting.Strategies.Feedback; using PChecker.SystematicTesting.Strategies.Probabilistic; -using PChecker.SystematicTesting.Strategies.Probabilistic.pctcp; using PChecker.SystematicTesting.Strategies.Special; using PChecker.SystematicTesting.Traces; using PChecker.Utilities; @@ -65,13 +65,15 @@ public class TestingEngine /// internal readonly ISchedulingStrategy Strategy; + /// + /// Pattern coverage observer if pattern is provided + /// private EventPatternObserver? _eventPatternObserver; - private ConflictOpMonitor? _conflictOpMonitor; - - private VectorClockWrapper? _vcWrapper; - - private AbstractScheduleObserver? _abstractScheduleObserver; + /// + /// Monitors conflict operations used by the POS Strategy. + /// + private ConflictOpMonitor? _conflictOpObserver; /// /// Random value generator used by the scheduling strategies. @@ -209,13 +211,14 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration, As if (checkerConfiguration.PatternSource.Length > 0) { var result = t.GetMethod(checkerConfiguration.PatternSource, - BindingFlags.Public | BindingFlags.Static)!; + BindingFlags.Public | BindingFlags.Static)!; eventMatcher = new EventPatternObserver(result); } } catch { - Error.ReportAndExit($"Failed to get test method '{checkerConfiguration.TestCaseName}' from assembly '{assembly.FullName}'"); + Error.ReportAndExit( + $"Failed to get test method '{checkerConfiguration.TestCaseName}' from assembly '{assembly.FullName}'"); } return new TestingEngine(checkerConfiguration, testMethodInfo, eventMatcher); @@ -248,7 +251,8 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration, Fu /// /// Creates a new systematic testing engine. /// - public static TestingEngine Create(CheckerConfiguration checkerConfiguration, Func test) => + public static TestingEngine Create(CheckerConfiguration checkerConfiguration, + Func test) => new TestingEngine(checkerConfiguration, test); /// @@ -273,7 +277,8 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo /// /// Initializes a new instance of the class. /// - private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo, EventPatternObserver observer) + private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo, + EventPatternObserver observer) { _checkerConfiguration = checkerConfiguration; TestMethodInfo = testMethodInfo; @@ -300,9 +305,10 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo { JsonVerboseLogs = new List>(); } - if (checkerConfiguration.EnableConflictAnalysis || checkerConfiguration.SchedulingStrategy == "rff") + + if (checkerConfiguration.EnableConflictAnalysis) { - _conflictOpMonitor = new ConflictOpMonitor(); + _conflictOpObserver = new ConflictOpMonitor(); } if (checkerConfiguration.SchedulingStrategy is "replay") @@ -322,24 +328,10 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, scheduler); } - else if (checkerConfiguration.SchedulingStrategy is "pctcp") - { - _vcWrapper = new VectorClockWrapper(); - var scheduler = new PCTCPScheduler(checkerConfiguration.StrategyBound, 0, - new RandomPriorizationProvider(RandomValueGenerator), _vcWrapper); - Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, - RandomValueGenerator, scheduler); - } else if (checkerConfiguration.SchedulingStrategy is "pos") { - var scheduler = new POSScheduler(new RandomPriorizationProvider(RandomValueGenerator), _conflictOpMonitor); - Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, - RandomValueGenerator, scheduler); - } - else if (checkerConfiguration.SchedulingStrategy is "rff") - { - _abstractScheduleObserver = new AbstractScheduleObserver(); - var scheduler = new RFFScheduler(RandomValueGenerator, _conflictOpMonitor, _abstractScheduleObserver); + var scheduler = new POSScheduler(new RandomPriorizationProvider(RandomValueGenerator), + _conflictOpObserver); Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, scheduler); } @@ -349,7 +341,8 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, new RandomPriorizationProvider(RandomValueGenerator)); var prefixStrategy = new PrioritizedSchedulingStrategy(prefixLength, RandomValueGenerator, scheduler); - var suffixStrategy = new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); + var suffixStrategy = + new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); Strategy = new ComboStrategy(prefixStrategy, suffixStrategy); } else if (checkerConfiguration.SchedulingStrategy is "probabilistic") @@ -369,32 +362,20 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo else if (checkerConfiguration.SchedulingStrategy is "feedback") { Strategy = new FeedbackGuidedStrategy( - _checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new RandomScheduleGenerator(checkerConfiguration)); - } - else if (checkerConfiguration.SchedulingStrategy is "2stagefeedback") - { - Strategy = new TwoStageFeedbackStrategy(_checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new RandomScheduleGenerator(checkerConfiguration)); + _checkerConfiguration, new RandomInputGenerator(checkerConfiguration), + new RandomScheduleGenerator(checkerConfiguration)); } else if (checkerConfiguration.SchedulingStrategy is "feedbackpct") { - Strategy = new FeedbackGuidedStrategy(_checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new PctScheduleGenerator(checkerConfiguration)); - } - else if (checkerConfiguration.SchedulingStrategy is "feedbackpctcp") - { - _vcWrapper = new VectorClockWrapper(); - Strategy = new FeedbackGuidedStrategy(_checkerConfiguration, new - RandomInputGenerator(checkerConfiguration), new PctcpScheduleGenerator(checkerConfiguration, _vcWrapper)); + Strategy = new FeedbackGuidedStrategy(_checkerConfiguration, + new RandomInputGenerator(checkerConfiguration), new PctScheduleGenerator(checkerConfiguration)); } else if (checkerConfiguration.SchedulingStrategy is "feedbackpos") { Strategy = new FeedbackGuidedStrategy( _checkerConfiguration, new RandomInputGenerator(checkerConfiguration), - new POSScheduleGenerator(_checkerConfiguration, _conflictOpMonitor)); - } - else if (checkerConfiguration.SchedulingStrategy is "2stagefeedbackpct") - { - Strategy = new TwoStageFeedbackStrategy(_checkerConfiguration, new RandomInputGenerator(checkerConfiguration), new PctScheduleGenerator(checkerConfiguration)); + new POSScheduleGenerator(_checkerConfiguration, _conflictOpObserver)); } else if (checkerConfiguration.SchedulingStrategy is "portfolio") { @@ -493,7 +474,6 @@ _checkerConfiguration.SchedulingStrategy is "probabilistic" || { try { - // Invokes the user-specified initialization method. TestMethodInfo.InitializeAllIterations(); watch = Stopwatch.StartNew(); @@ -510,7 +490,8 @@ _checkerConfiguration.SchedulingStrategy is "probabilistic" || RunNextIteration(i); if (IsReplayModeEnabled || (!_checkerConfiguration.PerformFullExploration && - TestReport.NumOfFoundBugs > 0) || !Strategy.PrepareForNextIteration()) + TestReport.NumOfFoundBugs > 0) || + !Strategy.PrepareForNextIteration()) { break; } @@ -554,7 +535,7 @@ _checkerConfiguration.SchedulingStrategy is "probabilistic" || var directory = _checkerConfiguration.OutputDirectory; var file = Path.GetFileNameWithoutExtension(_checkerConfiguration.AssemblyToBeAnalyzed); file += "_" + _checkerConfiguration.TestingProcessId; - var jsonVerbosePath = directory + file + "_verbose.trace.json"; + var jsonVerbosePath = directory + file + "_verbose.trace.json"; Logger.WriteLine("... Emitting verbose logs:"); Logger.WriteLine($"..... Writing {jsonVerbosePath}"); @@ -563,10 +544,29 @@ _checkerConfiguration.SchedulingStrategy is "probabilistic" || using var jsonStreamFile = File.Create(jsonVerbosePath); JsonSerializer.Serialize(jsonStreamFile, JsonVerboseLogs, jsonSerializerConfig); } - }, CancellationTokenSource.Token); } + /// + /// Register required observers. + /// + private void RegisterObservers(ControlledRuntime runtime) + { + // Always output a json log of the error + JsonLogger = new JsonWriter(); + runtime.SetJsonLogger(JsonLogger); + if (_eventPatternObserver != null) + { + runtime.RegisterLog(_eventPatternObserver); + } + + if (_conflictOpObserver != null) + { + _conflictOpObserver.VectorClockGenerator = JsonLogger.VcGenerator; + runtime.RegisterLog(_conflictOpObserver); + } + } + /// /// Runs the next testing schedule. /// @@ -586,6 +586,8 @@ private void RunNextIteration(int schedule) // Runtime used to serialize and test the program in this schedule. ControlledRuntime runtime = null; + TimelineObserver timelineObserver = new TimelineObserver(); + // Logger used to intercept the program output if no custom logger // is installed and if verbosity is turned off. InMemoryLogger runtimeLogger = null; @@ -599,27 +601,10 @@ private void RunNextIteration(int schedule) ShouldEmitTrace = false; // Creates a new instance of the controlled runtime. runtime = new ControlledRuntime(_checkerConfiguration, Strategy, RandomValueGenerator); - if (_conflictOpMonitor != null) - { - runtime.SendEventMonitors.Add(_conflictOpMonitor); - } - if (_abstractScheduleObserver != null) - { - runtime.SendEventMonitors.Add(_abstractScheduleObserver); - } - if (_eventPatternObserver != null) - { - runtime.RegisterLog(_eventPatternObserver); - } - // Always output a json log of the error - JsonLogger = new JsonWriter(); - runtime.SetJsonLogger(JsonLogger); + runtime.RegisterLog(timelineObserver); + RegisterObservers(runtime); - if (_vcWrapper != null) - { - _vcWrapper.CurrentVC = JsonLogger.VcGenerator; - } // If verbosity is turned off, then intercept the program log, and also redirect // the standard output and error streams to a nul logger. @@ -650,7 +635,7 @@ private void RunNextIteration(int schedule) if (Strategy is IFeedbackGuidedStrategy strategy) { - strategy.ObserveRunningResults(_eventPatternObserver, runtime); + strategy.ObserveRunningResults(_eventPatternObserver, timelineObserver); } // Checks that no monitor is in a hot state at termination. Only @@ -673,7 +658,7 @@ private void RunNextIteration(int schedule) runtime.LogWriter.LogCompletion(); - GatherTestingStatistics(runtime); + GatherTestingStatistics(runtime, timelineObserver); if (ShouldEmitTrace || (!IsReplayModeEnabled && TestReport.NumOfFoundBugs > 0)) { @@ -689,7 +674,6 @@ private void RunNextIteration(int schedule) TryEmitTraces(_checkerConfiguration.OutputDirectory, "trace_0"); } } - } finally { @@ -724,10 +708,11 @@ private void RunNextIteration(int schedule) { runtime.RemoveLog(_eventPatternObserver); } + runtimeLogger?.Dispose(); runtime?.Dispose(); _eventPatternObserver?.Reset(); - _conflictOpMonitor?.Reset(); + _conflictOpObserver?.Reset(); } } @@ -757,37 +742,42 @@ public string GetReport() return TestReport.GetText(_checkerConfiguration, "..."); } - public void TryEmitTimeline(TimelineObserver observer) - { - TimelineFileStream.WriteLine(observer.GetTimeline()); - } - /// /// Returns an object where the value null is replaced with "null" /// - public object RecursivelyReplaceNullWithString(object obj) { - if (obj == null) { + public object RecursivelyReplaceNullWithString(object obj) + { + if (obj == null) + { return "null"; } - if (obj is Dictionary dictionary) { + + if (obj is Dictionary dictionary) + { var newDictionary = new Dictionary(); - foreach (var item in dictionary) { + foreach (var item in dictionary) + { var newVal = RecursivelyReplaceNullWithString(item.Value); if (newVal != null) newDictionary[item.Key] = newVal; } + return newDictionary; } - else if (obj is List list) { + else if (obj is List list) + { var newList = new List(); - foreach (var item in list) { + foreach (var item in list) + { var newItem = RecursivelyReplaceNullWithString(item); if (newItem != null) newList.Add(newItem); } + return newList; } - else { + else + { return obj; } } @@ -842,8 +832,10 @@ public void TryEmitTraces(string directory, string file) Logger.WriteLine($"..... Writing {jsonPath}"); // Remove the null objects from payload recursively for each log event - for(int i=0; i /// Gathers the exploration strategy statistics from the specified runtimne. /// - private void GatherTestingStatistics(ControlledRuntime runtime) + private void GatherTestingStatistics(ControlledRuntime runtime, TimelineObserver timelineObserver) { var report = runtime.Scheduler.GetReport(); if (_checkerConfiguration.ReportActivityCoverage) @@ -1017,7 +1009,6 @@ private void GatherTestingStatistics(ControlledRuntime runtime) shouldSave = _eventPatternObserver.ShouldSave(); TestReport.ValidScheduling.TryAdd(shouldSave, 0); TestReport.ValidScheduling[shouldSave] += 1; - } if (shouldSave == 1) @@ -1026,15 +1017,7 @@ private void GatherTestingStatistics(ControlledRuntime runtime) report.CoverageInfo.Merge(coverageInfo); TestReport.Merge(report); - if (TestReport.ExploredTimelines.Add(runtime.TimelineObserver.GetTimelineHash())) - { - // if (_checkerConfiguration.IsVerbose) - // { - // Logger.WriteLine($"... New timeline observed: {runtime.TimelineObserver.GetTimeline()}"); - // } - TryEmitTimeline(runtime.TimelineObserver); - ShouldEmitTrace = true; - } + TestReport.ExploredTimelines.Add(timelineObserver.GetTimelineHash()); // Also save the graph snapshot of the last iteration, if there is one. Graph = coverageInfo.CoverageGraph; // Also save the graph snapshot of the last schedule, if there is one. @@ -1057,15 +1040,13 @@ private void ConstructReproducableTrace(ControlledRuntime runtime) if (_checkerConfiguration.IsLivenessCheckingEnabled) { stringBuilder.Append("--liveness-temperature-threshold:" + - _checkerConfiguration.LivenessTemperatureThreshold). - Append(Environment.NewLine); + _checkerConfiguration.LivenessTemperatureThreshold).Append(Environment.NewLine); } if (!string.IsNullOrEmpty(_checkerConfiguration.TestCaseName)) { stringBuilder.Append("--test-method:" + - _checkerConfiguration.TestCaseName). - Append(Environment.NewLine); + _checkerConfiguration.TestCaseName).Append(Environment.NewLine); } for (var idx = 0; idx < runtime.Scheduler.ScheduleTrace.Count; idx++) @@ -1101,7 +1082,9 @@ private string[] GetScheduleForReplay(out bool isFair) string[] scheduleDump; if (_checkerConfiguration.ScheduleTrace.Length > 0) { - scheduleDump = _checkerConfiguration.ScheduleTrace.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + scheduleDump = + _checkerConfiguration.ScheduleTrace.Split(new string[] { Environment.NewLine }, + StringSplitOptions.None); } else { diff --git a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs index fa2bd5fb1a..398bee9cfc 100644 --- a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs +++ b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs @@ -153,7 +153,6 @@ private void WriteSourcePrologue(CompilationContext context, StringWriter output { context.WriteLine(output, "using PChecker;"); context.WriteLine(output, "using PChecker.Actors;"); - context.WriteLine(output, "using PChecker.Matcher;"); context.WriteLine(output, "using PChecker.Actors.Events;"); context.WriteLine(output, "using PChecker.Runtime;"); context.WriteLine(output, "using PChecker.Specifications;"); @@ -724,7 +723,7 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func } else if (function.Role == FunctionRole.Scenario) { - functionParameters = "List events"; + functionParameters = "List events"; } else { @@ -811,7 +810,7 @@ private void WriteScenario(CompilationContext context, StringWriter output, Func int numOfStmt = function.Body.Statements.Count + 1; context.WriteLine(output, $"int state = {numOfStmt};"); var eventPredicates = string.Join(" or ", function.Signature.ParameterEvents.Select(it => it.Name)); - context.WriteLine(output, $"events = events.Where(it => it.Event is {eventPredicates}).ToList();"); + context.WriteLine(output, $"events = events.Where(it => it is {eventPredicates}).ToList();"); WriteConstraintsRecursive(context, output, function, 0, new HashSet(), 0); context.WriteLine(output, "return state;"); } @@ -830,8 +829,8 @@ private void WriteConstraintsRecursive(CompilationContext context, StringWriter var paramName = context.Names.GetNameForDecl(param); context.WriteLine(output, $"for (var i{index} = {start} ; i{index} < events.Count; i{index} ++) " + "{"); context.WriteLine(output, $"var {paramName}_obj = events[i{index}];"); - context.WriteLine(output, $"if ({paramName}_obj.Event is not {e.Name}) continue;"); - context.WriteLine(output, $"var {paramName} = ((PEvent) {paramName}_obj.Event).Payload;"); + context.WriteLine(output, $"if ({paramName}_obj is not {e.Name}) continue;"); + context.WriteLine(output, $"var {paramName} = ((PEvent) {paramName}_obj).Payload;"); foreach (var bodyStatement in function.Body.Statements) { diff --git a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs index f50d9eefa1..22eb088be1 100644 --- a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs +++ b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs @@ -55,21 +55,15 @@ internal PCheckerOptions() var schedulingGroup = Parser.GetOrCreateGroup("scheduling", "Search prioritization options"); schedulingGroup.AddArgument("sch-random", null, "Choose the random scheduling strategy (this is the default)", typeof(bool)); schedulingGroup.AddArgument("sch-feedback", null, "Choose the random scheduling strategy with feedback mutation", typeof(bool)); - schedulingGroup.AddArgument("sch-2stagefeedback", null, "Choose the random scheduling strategy with 2 stage feedback mutation", typeof(bool)); schedulingGroup.AddArgument("sch-feedbackpct", null, "Choose the PCT scheduling strategy with feedback mutation", typeof(uint)); - schedulingGroup.AddArgument("sch-feedbackpctcp", null, "Choose the PCT scheduling strategy with feedback mutation", typeof(uint)); schedulingGroup.AddArgument("sch-feedbackpos", null, - "Choose the PCT scheduling strategy with feedback mutation", typeof(bool)); - schedulingGroup.AddArgument("sch-rff", null, "Choose the RFF scheduling strategy", typeof(bool)); - schedulingGroup.AddArgument("sch-2stagefeedbackpct", null, "Choose the PCT scheduling strategy with 2 stage feedback mutation", typeof(uint)); + "Choose the POS scheduling strategy with feedback mutation", typeof(bool)); schedulingGroup.AddArgument("sch-probabilistic", "sp", "Choose the probabilistic scheduling strategy with given probability for each scheduling decision where the probability is " + "specified as the integer N in the equation 0.5 to the power of N. So for N=1, the probability is 0.5, for N=2 the probability is 0.25, N=3 you get 0.125, etc.", typeof(uint)); schedulingGroup.AddArgument("sch-pct", null, "Choose the PCT scheduling strategy with given maximum number of priority switch points", typeof(uint)); - schedulingGroup.AddArgument("sch-pctcp", null, "Choose the PCT scheduling strategy with given maximum number of priority switch points", typeof(uint)); - schedulingGroup.AddArgument("sch-pos", null, - "Choose the PCT scheduling strategy with given maximum number of priority switch points", typeof(bool)); + schedulingGroup.AddArgument("sch-pos", null, "Choose the POS scheduling strategy", typeof(bool)); schedulingGroup.AddArgument("sch-fairpct", null, "Choose the fair PCT scheduling strategy with given maximum number of priority switch points", typeof(uint)); schedulingGroup.AddArgument("sch-rl", null, "Choose the reinforcement learning (RL) scheduling strategy", typeof(bool)).IsHidden = true; var schCoverage = schedulingGroup.AddArgument("sch-coverage", null, "Choose the scheduling strategy for coverage mode (options: learn, random, dfs, stateless). (default: learn)"); @@ -88,11 +82,7 @@ internal PCheckerOptions() advancedGroup.AddArgument("psym-args", null, "Specify a concatenated list of additional PSym-specific arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("jvm-args", null, "Specify a concatenated list of PSym-specific JVM arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("pattern", null, "The name of the pattern matcher generator", typeof(string)); - advancedGroup.AddArgument("no-partial-match", null, "For feedback strategy, do not save a schedule if the pattern is partially matched", typeof(bool)); advancedGroup.AddArgument("discard-after", null, "For feedback strategy, discard saved generators after saving N inputs", typeof(int)); - advancedGroup.AddArgument("fixed-priority", null, "For feedback strategy, schedule generator mutations based on diversity", typeof(bool)); - advancedGroup.AddArgument("ignore-pattern", null, "For feedback strategy, ignore the pattern feedback", typeof(bool)); - advancedGroup.AddArgument("no-priority-based", null, "For feedback strategy, disable priority based sampling.", typeof(bool)); advancedGroup.AddArgument("conflict-analysis", null, "Enable POS conflict analysis.", typeof(bool)); } @@ -342,21 +332,12 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c case "pattern": checkerConfiguration.PatternSource = (string) option.Value; break; - case "no-partial-match": - checkerConfiguration.SavePartialMatch = false; - break; case "discard-after": checkerConfiguration.DiscardAfter = (int) option.Value; break; case "fixed-priority": checkerConfiguration.DiversityBasedPriority = false; break; - case "ignore-pattern": - checkerConfiguration.IgnorePatternFeedback = true; - break; - case "no-priority-based": - checkerConfiguration.PriorityBasedSampling = false; - break; case "conflict-analysis": checkerConfiguration.EnableConflictAnalysis = true; break; @@ -386,13 +367,9 @@ private static void SanitizeConfiguration(CheckerConfiguration checkerConfigurat if (checkerConfiguration.SchedulingStrategy != "portfolio" && checkerConfiguration.SchedulingStrategy != "random" && - checkerConfiguration.SchedulingStrategy != "pctcp" && checkerConfiguration.SchedulingStrategy != "feedback" && checkerConfiguration.SchedulingStrategy != "feedbackpct" && - checkerConfiguration.SchedulingStrategy != "feedbackpctcp" && checkerConfiguration.SchedulingStrategy != "feedbackpos" && - checkerConfiguration.SchedulingStrategy != "2stagefeedback" && - checkerConfiguration.SchedulingStrategy != "2stagefeedbackpct" && checkerConfiguration.SchedulingStrategy != "pct" && checkerConfiguration.SchedulingStrategy != "pos" && checkerConfiguration.SchedulingStrategy != "fairpct" && @@ -401,7 +378,6 @@ private static void SanitizeConfiguration(CheckerConfiguration checkerConfigurat checkerConfiguration.SchedulingStrategy != "replay" && checkerConfiguration.SchedulingStrategy != "learn" && checkerConfiguration.SchedulingStrategy != "dfs" && - checkerConfiguration.SchedulingStrategy != "rff" && checkerConfiguration.SchedulingStrategy != "stateless") { Error.CheckerReportAndExit("Please provide a scheduling strategy (see --sch* options)"); } diff --git a/Src/PRuntimes/PCSharpRuntime/PMachine.cs b/Src/PRuntimes/PCSharpRuntime/PMachine.cs index c8300ef401..2816a97d01 100644 --- a/Src/PRuntimes/PCSharpRuntime/PMachine.cs +++ b/Src/PRuntimes/PCSharpRuntime/PMachine.cs @@ -86,6 +86,8 @@ public void TrySendEvent(PMachineValue target, Event ev, object payload = null) var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); ev = (Event)oneArgConstructor.Invoke(new[] { payload , ev.Loc}); + ev.Sender = Id.ToString(); + ev.Receiver = target.Id.ToString(); AnnounceInternal(ev); SendEvent(target.Id, ev); } From 3f4781a29a524833be808a1901fe6800aa323e65 Mon Sep 17 00:00:00 2001 From: Ao Li <5557706+aoli-al@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:05:34 -0400 Subject: [PATCH 03/21] PR cleanup (#723) * Added support for generating a warning when spec handles an event but does not add it in its observes list (#716) * Create custom converter for JSON serialization in .NET8 (#717) * Create custom converter for JSON serialization in .NET8 * Add check for different dictionary type for null replacement --------- Co-authored-by: Eric Hua --------- Co-authored-by: Ankush Desai Co-authored-by: Eric Hua Co-authored-by: Eric Hua --- .../SystematicTesting/TestingEngine.cs | 36 +++++++++++++++---- .../DefaultTranslationErrorHandler.cs | 5 +++ .../CompilerCore/ITranslationErrorHandler.cs | 2 ++ .../CompilerCore/TypeChecker/Analyzer.cs | 2 +- .../TypeChecker/MachineChecker.cs | 23 +++++++++++- .../Correct/monitorobserves/observes.p | 14 ++++++++ 6 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 Tst/RegressionTests/Feature1SMLevelDecls/Correct/monitorobserves/observes.p diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index 876873d3e3..18042472c6 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -113,9 +113,25 @@ public class TestingEngine { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - WriteIndented = true + WriteIndented = true, + Converters = { new EncodingConverter() } }; + internal class EncodingConverter : JsonConverter + { + public override Encoding Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var name = reader.GetString(); + if (name == null) + return null; + return Encoding.GetEncoding(name); + } + public override void Write(Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.WebName); + } + } + /// /// The profiler. /// @@ -751,12 +767,18 @@ public object RecursivelyReplaceNullWithString(object obj) { return "null"; } - - if (obj is Dictionary dictionary) - { + if (obj is Dictionary dictionaryStr) { var newDictionary = new Dictionary(); - foreach (var item in dictionary) - { + foreach (var item in dictionaryStr) { + var newVal = RecursivelyReplaceNullWithString(item.Value); + if (newVal != null) + newDictionary[item.Key] = newVal; + } + return newDictionary; + } + else if (obj is Dictionary dictionaryInt) { + var newDictionary = new Dictionary(); + foreach (var item in dictionaryInt) { var newVal = RecursivelyReplaceNullWithString(item.Value); if (newVal != null) newDictionary[item.Key] = newVal; @@ -1156,4 +1178,4 @@ public void SetLogger(TextWriter logger) ErrorReporter.Logger = logger; } } -} \ No newline at end of file +} diff --git a/Src/PCompiler/CompilerCore/DefaultTranslationErrorHandler.cs b/Src/PCompiler/CompilerCore/DefaultTranslationErrorHandler.cs index ccacf6fcfd..4aef965ae9 100644 --- a/Src/PCompiler/CompilerCore/DefaultTranslationErrorHandler.cs +++ b/Src/PCompiler/CompilerCore/DefaultTranslationErrorHandler.cs @@ -334,5 +334,10 @@ private string DeclarationName(IPDecl method) { return method.Name.Length > 0 ? method.Name : $"at {locationResolver.GetLocation(method.SourceLocation)}"; } + + public string SpecObservesSetIncompleteWarning(ParserRuleContext ctx, PEvent ev, Machine machine) + { + return $"[!Warning!]\n[{locationResolver.GetLocation(ctx, ctx.start)}] Event {ev.Name} is not in the observes list of the spec machine {machine.Name}. The event-handler is never triggered as the event is not observed by the spec.\n[!Warning!]"; + } } } \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/ITranslationErrorHandler.cs b/Src/PCompiler/CompilerCore/ITranslationErrorHandler.cs index 00e12c244f..402a5fc5d6 100644 --- a/Src/PCompiler/CompilerCore/ITranslationErrorHandler.cs +++ b/Src/PCompiler/CompilerCore/ITranslationErrorHandler.cs @@ -117,5 +117,7 @@ Exception DuplicateStartState( Exception IllegalChooseSubExprType(PParser.ChooseExprContext context, PLanguageType subExprType); Exception IllegalFunctionUsedInSpecMachine(Function function, Machine callerOwner); + + String SpecObservesSetIncompleteWarning(ParserRuleContext loc, PEvent ev, Machine machine); } } \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs b/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs index aac694afb0..2257d5a6a9 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs @@ -20,7 +20,7 @@ public static Scope AnalyzeCompilationUnit(ICompilerConfiguration config, // Step 2: Validate machine specifications foreach (var machine in globalScope.Machines) { - MachineChecker.Validate(handler, machine); + MachineChecker.Validate(handler, machine, config); } // Step 3: Fill function bodies diff --git a/Src/PCompiler/CompilerCore/TypeChecker/MachineChecker.cs b/Src/PCompiler/CompilerCore/TypeChecker/MachineChecker.cs index 628d9cac50..b8f0a5cddf 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/MachineChecker.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/MachineChecker.cs @@ -11,13 +11,34 @@ namespace Plang.Compiler.TypeChecker { public static class MachineChecker { - public static void Validate(ITranslationErrorHandler handler, Machine machine) + public static void Validate(ITranslationErrorHandler handler, Machine machine, ICompilerConfiguration job) { var startState = FindStartState(machine, handler); var startStatePayloadType = GetStatePayload(startState); Debug.Assert(startStatePayloadType.IsSameTypeAs(machine.PayloadType)); ValidateHandlers(handler, machine); ValidateTransitions(handler, machine); + // special validation for monitors: + // ensure that each eventhandler is in the observe set. + ValidateSpecObservesList(handler, machine, job); + } + + private static void ValidateSpecObservesList(ITranslationErrorHandler handler, Machine machine, ICompilerConfiguration job) + { + if (machine.IsSpec) + { + foreach (var state in machine.AllStates()) + { + foreach (var pair in state.AllEventHandlers) + { + if (!machine.Observes.Events.Contains(pair.Key)) + { + job.Output.WriteWarning( + handler.SpecObservesSetIncompleteWarning(pair.Value.SourceLocation, pair.Key, machine)); + } + } + } + } } private static void ValidateHandlers(ITranslationErrorHandler handler, Machine machine) diff --git a/Tst/RegressionTests/Feature1SMLevelDecls/Correct/monitorobserves/observes.p b/Tst/RegressionTests/Feature1SMLevelDecls/Correct/monitorobserves/observes.p new file mode 100644 index 0000000000..03855f7148 --- /dev/null +++ b/Tst/RegressionTests/Feature1SMLevelDecls/Correct/monitorobserves/observes.p @@ -0,0 +1,14 @@ +event e1; +event e2; + +spec Invariant1 observes e1 { + start state Init { + on e2 goto Init; + } +} + +machine Main { + start state Init { + + } +} \ No newline at end of file From 9241ed4c6cff8f11985d3c009df9474237b6e115 Mon Sep 17 00:00:00 2001 From: Ankush Desai Date: Mon, 30 Sep 2024 17:08:29 -0700 Subject: [PATCH 04/21] Merge Recent Bug Fixes (#778) * Added support for generating a warning when spec handles an event but does not add it in its observes list (#716) * Create custom converter for JSON serialization in .NET8 (#717) * Create custom converter for JSON serialization in .NET8 * Add check for different dictionary type for null replacement --------- Co-authored-by: Eric Hua * udpate. * update. * update. --------- Co-authored-by: Eric Hua Co-authored-by: Eric Hua Co-authored-by: Ao Li --- Src/PChecker/CheckerCore/CheckerConfiguration.cs | 2 +- Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs | 2 +- Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index 57706a2b4f..b1f1831800 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -229,7 +229,7 @@ public int MaxSchedulingSteps /// Defaults to true. /// [DataMember] - public bool IsJsonLogEnabled { get; set; } = true; + public bool IsJsonLogEnabled { get; set; } = false; /// /// If specified, requests a custom runtime log to be used instead of the default. diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs index 24fd70057b..36b59c3052 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs @@ -105,7 +105,7 @@ public class TestReport /// Set of hashes of timelines discovered by the scheduler. /// [DataMember] - public HashSet ExploredTimelines = new(); + public Dictionary ExploredTimelines = new(); /// /// Number of schedulings that satisfies the pattern. diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index 18042472c6..5d8088d730 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -1038,8 +1038,9 @@ private void GatherTestingStatistics(ControlledRuntime runtime, TimelineObserver var coverageInfo = runtime.GetCoverageInfo(); report.CoverageInfo.Merge(coverageInfo); TestReport.Merge(report); - - TestReport.ExploredTimelines.Add(timelineObserver.GetTimelineHash()); + var timelineHash = timelineObserver.GetTimelineHash(); + TestReport.ExploredTimelines[timelineHash] = + TestReport.ExploredTimelines.GetValueOrDefault(timelineHash, 0) + 1; // Also save the graph snapshot of the last iteration, if there is one. Graph = coverageInfo.CoverageGraph; // Also save the graph snapshot of the last schedule, if there is one. From 944687422887dd4f5aaa692b54cb206128d3ffde Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 3 Oct 2024 08:37:47 +0800 Subject: [PATCH 05/21] Fix feedback strategy and remove experiment features. --- .../CheckerCore/CheckerConfiguration.cs | 14 ----------- .../Feedback/FeedbackGuidedStrategy.cs | 17 ++++---------- .../Probabilistic/QLearningStrategy.cs | 23 +------------------ .../SystematicTesting/TestingEngine.cs | 3 +-- .../PCommandLine/Options/PCheckerOptions.cs | 7 ------ 5 files changed, 7 insertions(+), 57 deletions(-) diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index b1f1831800..1107cafff4 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -292,18 +292,6 @@ public int MaxSchedulingSteps [DataMember] public string JvmArgs; - /// - /// For feedback strategy, discard saved generators if the size of the buffer is greater than N. - /// - [DataMember] - public int DiscardAfter; - - /// - /// For QL strategy, schedule generator mutations based on diversity. - /// - [DataMember] - public bool DiversityBasedPriority; - /// /// Enable conflict analysis for scheduling optimization. /// @@ -355,8 +343,6 @@ protected CheckerConfiguration() EnableColoredConsoleOutput = false; DisableEnvironmentExit = true; - DiscardAfter = 100; - DiversityBasedPriority = true; EnableConflictAnalysis = false; PSymArgs = ""; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs index 358bfe3c9f..bb53639daf 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs @@ -32,7 +32,6 @@ public record GeneratorRecord(int Priority, StrategyGenerator Generator, List _visitedGenerators = new HashSet(); private GeneratorRecord? _currentParent = null; - private readonly int _discardAfter; private System.Random _rnd = new System.Random(); @@ -51,7 +50,6 @@ public FeedbackGuidedStrategy(CheckerConfiguration checkerConfiguration, TInput _maxScheduledSteps = checkerConfiguration.MaxFairSchedulingSteps; } Generator = new StrategyGenerator(input, schedule); - _discardAfter = checkerConfiguration.DiscardAfter; } /// @@ -151,7 +149,7 @@ private int ComputeDiversity(int timeline, List hash) } - return (hash.Count - maxSim) * 10 + 20; + return (hash.Count - maxSim) + 20; } /// @@ -204,13 +202,6 @@ public virtual void ObserveRunningResults(EventPatternObserver patternObserver, { _savedGenerators.Insert(index, record); } - - if (_savedGenerators.Count > _discardAfter) - { - var last = _savedGenerators.Last(); - _visitedGenerators.Remove(last); - _savedGenerators.RemoveAt(_savedGenerators.Count - 1); - } } } @@ -245,8 +236,9 @@ private void PrepareNextInput() if (_visitedGenerators.Contains(generator)) continue; _currentParent = generator; _visitedGenerators.Add(generator); - _pendingMutations = 50; + _pendingMutations = generator.Priority; found = true; + break; } if (!found) @@ -256,13 +248,14 @@ private void PrepareNextInput() _visitedGenerators.Clear(); _currentParent = _savedGenerators.First(); _visitedGenerators.Add(_currentParent); + _pendingMutations = _currentParent.Priority; } else { _shouldExploreNew = true; _currentParent = null; + _pendingMutations = 50; } - _pendingMutations = 50; } } diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs index 64ed1674c0..c6943ff3a8 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs @@ -81,13 +81,12 @@ internal class QLearningStrategy : RandomStrategy, IFeedbackGuidedStrategy /// private int Epochs; - private bool _diversityFeedback; /// /// Initializes a new instance of the class. /// It uses the specified random number generator. /// - public QLearningStrategy(int maxSteps, IRandomValueGenerator random, bool diversityFeedback) + public QLearningStrategy(int maxSteps, IRandomValueGenerator random) : base(maxSteps, random) { this.OperationQTable = new Dictionary>(); @@ -102,7 +101,6 @@ public QLearningStrategy(int maxSteps, IRandomValueGenerator random, bool divers this.FailureInjectionReward = -1000; this.BasicActionReward = -1; this.Epochs = 0; - _diversityFeedback = diversityFeedback; } /// @@ -403,25 +401,6 @@ public void ObserveRunningResults(EventPatternObserver patternObserver, Timeline int priority = 1; - if (_diversityFeedback) - { - int diversityScore = ComputeDiversity(timelineHash, timelineMinhash); - if (patternObserver == null) - { - priority = diversityScore; - } - else - { - int coverageResult = patternObserver.ShouldSave(); - priority = diversityScore / coverageResult; - } - - if (priority != 0) - { - _savedTimelines.Add(timelineMinhash); - } - priority += 1; - } var node = this.ExecutionPath.First; while (node != null && node.Next != null) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index 5d8088d730..7d260ec8e9 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -368,8 +368,7 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "rl") { - Strategy = new QLearningStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, - checkerConfiguration.DiversityBasedPriority); + Strategy = new QLearningStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator); } else if (checkerConfiguration.SchedulingStrategy is "dfs") { diff --git a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs index 22eb088be1..b98bd89831 100644 --- a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs +++ b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs @@ -82,7 +82,6 @@ internal PCheckerOptions() advancedGroup.AddArgument("psym-args", null, "Specify a concatenated list of additional PSym-specific arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("jvm-args", null, "Specify a concatenated list of PSym-specific JVM arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("pattern", null, "The name of the pattern matcher generator", typeof(string)); - advancedGroup.AddArgument("discard-after", null, "For feedback strategy, discard saved generators after saving N inputs", typeof(int)); advancedGroup.AddArgument("conflict-analysis", null, "Enable POS conflict analysis.", typeof(bool)); } @@ -332,12 +331,6 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c case "pattern": checkerConfiguration.PatternSource = (string) option.Value; break; - case "discard-after": - checkerConfiguration.DiscardAfter = (int) option.Value; - break; - case "fixed-priority": - checkerConfiguration.DiversityBasedPriority = false; - break; case "conflict-analysis": checkerConfiguration.EnableConflictAnalysis = true; break; From 9d91a5459291ea50d954fe00c728a10d10a19311 Mon Sep 17 00:00:00 2001 From: ChristineZh0u <48167738+ChristineZh0u@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:32:57 -0700 Subject: [PATCH 06/21] Removing Pattern (#786) Co-authored-by: Christine Zhou --- .../CheckerCore/CheckerConfiguration.cs | 7 -- .../Feedback/Coverage/EventPatternObserver.cs | 51 -------------- .../Feedback/FeedbackGuidedStrategy.cs | 16 +---- .../Feedback/IFeedbackGuidedStrategy.cs | 2 +- .../Probabilistic/QLearningStrategy.cs | 2 +- .../SystematicTesting/TestReport.cs | 9 +-- .../SystematicTesting/TestingEngine.cs | 69 ++++--------------- .../PCommandLine/Options/PCheckerOptions.cs | 4 -- 8 files changed, 21 insertions(+), 139 deletions(-) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index 1107cafff4..bcb09e5977 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -257,12 +257,6 @@ public int MaxSchedulingSteps [DataMember] public uint TestingProcessId; - /// - /// The source of the pattern generator. - /// - [DataMember] - public string PatternSource; - /// /// Additional assembly specifications to instrument for code coverage, besides those in the /// dependency graph between and the Microsoft.Coyote DLLs. @@ -347,7 +341,6 @@ protected CheckerConfiguration() PSymArgs = ""; JvmArgs = ""; - PatternSource = ""; } /// diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs deleted file mode 100644 index 61b74b57ea..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/EventPatternObserver.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using PChecker.Actors; -using PChecker.Actors.Events; -using PChecker.Actors.Logging; - -namespace PChecker.Feedback; - -internal class EventPatternObserver : ActorRuntimeLogBase -{ - private MethodInfo _matcher; - private List _events = new(); - - public EventPatternObserver(MethodInfo matcher) - { - _matcher = matcher; - } - - public override void OnDequeueEvent(ActorId id, string stateName, Event e) - { - e.Index = _events.Count; - e.State = stateName; - _events.Add(e); - } - - public override void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, - string senderStateName, Event e) - { - e.Index = _events.Count; - e.State = stateName; - _events.Add(e); - } - - public virtual int ShouldSave() - { - return (int) _matcher.Invoke(null, new [] { _events }); - } - - public virtual bool IsMatched() - { - int result = (int) _matcher.Invoke(null, new [] { _events }); - return result == 1; - } - - - public void Reset() - { - _events.Clear(); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs index bb53639daf..8567bc6539 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs @@ -155,7 +155,7 @@ private int ComputeDiversity(int timeline, List hash) /// /// This method observes the results of previous run and prepare for the next run. /// - public virtual void ObserveRunningResults(EventPatternObserver patternObserver, TimelineObserver timelineObserver) + public virtual void ObserveRunningResults(TimelineObserver timelineObserver) { var timelineHash = timelineObserver.GetTimelineHash(); var timelineMinhash = timelineObserver.GetTimelineMinhash(); @@ -167,18 +167,8 @@ public virtual void ObserveRunningResults(EventPatternObserver patternObserver, return; } - int priority = 0; - if (patternObserver == null) - { - priority = diversityScore; - } - else - { - int coverageResult = patternObserver.ShouldSave(); - double coverageScore = 1.0 / coverageResult; - priority = (int)(diversityScore * coverageScore); - } - + int priority = diversityScore; + if (priority > 0) { var record = new GeneratorRecord(priority, Generator, timelineMinhash); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs index f74b8b8b35..ad3c3c8242 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IFeedbackGuidedStrategy.cs @@ -6,7 +6,7 @@ namespace PChecker.SystematicTesting.Strategies.Feedback; internal interface IFeedbackGuidedStrategy: ISchedulingStrategy { - public void ObserveRunningResults(EventPatternObserver patternObserver, TimelineObserver timelineObserver); + public void ObserveRunningResults(TimelineObserver timelineObserver); public int TotalSavedInputs(); public void DumpStats(TextWriter writer); } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs index c6943ff3a8..79b29448c4 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs @@ -394,7 +394,7 @@ private int ComputeDiversity(int timeline, List hash) return (hash.Count - maxSim) * 10 + 20; } - public void ObserveRunningResults(EventPatternObserver patternObserver, TimelineObserver timelineObserver) + public void ObserveRunningResults(TimelineObserver timelineObserver) { var timelineHash = timelineObserver.GetTimelineHash(); var timelineMinhash = timelineObserver.GetTimelineMinhash(); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs index 20e66679c4..0ad3f3d046 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs @@ -100,20 +100,13 @@ public class TestReport /// [DataMember] public HashSet InternalErrors { get; internal set; } - - + /// /// Set of hashes of timelines discovered by the scheduler. /// [DataMember] public Dictionary ExploredTimelines = new(); - /// - /// Number of schedulings that satisfies the pattern. - /// - [DataMember] - public Dictionary ValidScheduling = new(); - /// /// Lock for the test report. /// diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index e42a83d2d4..d226141c10 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -65,11 +65,6 @@ public class TestingEngine /// internal readonly ISchedulingStrategy Strategy; - /// - /// Pattern coverage observer if pattern is provided - /// - private EventPatternObserver? _eventPatternObserver; - /// /// Monitors conflict operations used by the POS Strategy. /// @@ -217,19 +212,12 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration, As } TestMethodInfo testMethodInfo = null; - EventPatternObserver eventMatcher = null; try { testMethodInfo = TestMethodInfo.GetFromAssembly(assembly, checkerConfiguration.TestCaseName); Console.Out.WriteLine($".. Test case :: {testMethodInfo.Name}"); Type t = assembly.GetType("PImplementation.GlobalFunctions"); - if (checkerConfiguration.PatternSource.Length > 0) - { - var result = t.GetMethod(checkerConfiguration.PatternSource, - BindingFlags.Public | BindingFlags.Static)!; - eventMatcher = new EventPatternObserver(result); - } } catch { @@ -237,7 +225,7 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration, As $"Failed to get test method '{checkerConfiguration.TestCaseName}' from assembly '{assembly.FullName}'"); } - return new TestingEngine(checkerConfiguration, testMethodInfo, eventMatcher); + return new TestingEngine(checkerConfiguration, testMethodInfo); } /// @@ -284,21 +272,15 @@ internal TestingEngine(CheckerConfiguration checkerConfiguration, Delegate test) : this(checkerConfiguration, new TestMethodInfo(test)) { } - - private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo) - : this(checkerConfiguration, testMethodInfo, null) - { - } + /// /// Initializes a new instance of the class. /// - private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo, - EventPatternObserver observer) + private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo testMethodInfo) { _checkerConfiguration = checkerConfiguration; TestMethodInfo = testMethodInfo; - _eventPatternObserver = observer; Logger = new ConsoleLogger(); ErrorReporter = new ErrorReporter(checkerConfiguration, Logger); @@ -586,10 +568,6 @@ private void RegisterObservers(ControlledRuntime runtime) // Always output a json log of the error JsonLogger = new JsonWriter(); runtime.SetJsonLogger(JsonLogger); - if (_eventPatternObserver != null) - { - runtime.RegisterLog(_eventPatternObserver); - } if (_conflictOpObserver != null) { @@ -666,7 +644,7 @@ private void RunNextIteration(int schedule) if (Strategy is IFeedbackGuidedStrategy strategy) { - strategy.ObserveRunningResults(_eventPatternObserver, timelineObserver); + strategy.ObserveRunningResults(timelineObserver); } // Checks that no monitor is in a hot state at termination. Only @@ -735,14 +713,9 @@ private void RunNextIteration(int schedule) } // Cleans up the runtime before the next iteration starts. - if (_eventPatternObserver != null) - { - runtime.RemoveLog(_eventPatternObserver); - } runtimeLogger?.Dispose(); runtime?.Dispose(); - _eventPatternObserver?.Reset(); _conflictOpObserver?.Reset(); } } @@ -1037,29 +1010,17 @@ private void GatherTestingStatistics(ControlledRuntime runtime, TimelineObserver { report.CoverageInfo.CoverageGraph = Graph; } - - int shouldSave = 1; - - if (_eventPatternObserver != null) - { - shouldSave = _eventPatternObserver.ShouldSave(); - TestReport.ValidScheduling.TryAdd(shouldSave, 0); - TestReport.ValidScheduling[shouldSave] += 1; - } - - if (shouldSave == 1) - { - var coverageInfo = runtime.GetCoverageInfo(); - report.CoverageInfo.Merge(coverageInfo); - TestReport.Merge(report); - var timelineHash = timelineObserver.GetTimelineHash(); - TestReport.ExploredTimelines[timelineHash] = - TestReport.ExploredTimelines.GetValueOrDefault(timelineHash, 0) + 1; - // Also save the graph snapshot of the last iteration, if there is one. - Graph = coverageInfo.CoverageGraph; - // Also save the graph snapshot of the last schedule, if there is one. - Graph = coverageInfo.CoverageGraph; - } + + var coverageInfo = runtime.GetCoverageInfo(); + report.CoverageInfo.Merge(coverageInfo); + TestReport.Merge(report); + var timelineHash = timelineObserver.GetTimelineHash(); + TestReport.ExploredTimelines[timelineHash] = + TestReport.ExploredTimelines.GetValueOrDefault(timelineHash, 0) + 1; + // Also save the graph snapshot of the last iteration, if there is one. + Graph = coverageInfo.CoverageGraph; + // Also save the graph snapshot of the last schedule, if there is one. + Graph = coverageInfo.CoverageGraph; } /// diff --git a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs index 80c601c616..407321454d 100644 --- a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs +++ b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs @@ -81,7 +81,6 @@ internal PCheckerOptions() advancedGroup.AddArgument("xml-trace", null, "Specify a filename for XML runtime log output to be written to", typeof(bool)); advancedGroup.AddArgument("psym-args", null, "Specify a concatenated list of additional PSym-specific arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("jvm-args", null, "Specify a concatenated list of PSym-specific JVM arguments to pass, each starting with a colon").IsHidden = true; - advancedGroup.AddArgument("pattern", null, "The name of the pattern matcher generator", typeof(string)); advancedGroup.AddArgument("conflict-analysis", null, "Enable POS conflict analysis.", typeof(bool)); } @@ -328,9 +327,6 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c case "jvm-args": checkerConfiguration.JvmArgs = ((string)option.Value).Replace(':', ' '); break; - case "pattern": - checkerConfiguration.PatternSource = (string) option.Value; - break; case "conflict-analysis": checkerConfiguration.EnableConflictAnalysis = true; break; From 5f65c5ca41cf94de4fe601ff358a11b3d230002f Mon Sep 17 00:00:00 2001 From: Christine Zhou Date: Tue, 8 Oct 2024 13:33:16 -0700 Subject: [PATCH 07/21] Remove conflict analysis --- .../CheckerCore/CheckerConfiguration.cs | 6 - .../Strategies/Feedback/ConflictOpMonitor.cs | 115 ------------------ .../Generator/Mutator/POSScheduleMutator.cs | 4 +- .../Generator/POSScheduleGenerator.cs | 16 +-- .../Strategies/Probabilistic/POSScheduler.cs | 12 +- .../PriorizationSchedulingBase.cs | 55 +-------- .../SystematicTesting/TestingEngine.cs | 23 +--- .../PCommandLine/Options/PCheckerOptions.cs | 4 - 8 files changed, 14 insertions(+), 221 deletions(-) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index bcb09e5977..bc296da11c 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -286,11 +286,6 @@ public int MaxSchedulingSteps [DataMember] public string JvmArgs; - /// - /// Enable conflict analysis for scheduling optimization. - /// - [DataMember] - public bool EnableConflictAnalysis; /// /// Initializes a new instance of the class. @@ -337,7 +332,6 @@ protected CheckerConfiguration() EnableColoredConsoleOutput = false; DisableEnvironmentExit = true; - EnableConflictAnalysis = false; PSymArgs = ""; JvmArgs = ""; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs deleted file mode 100644 index 00cef43310..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ConflictOpMonitor.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PChecker.Actors; -using PChecker.Actors.Events; -using PChecker.Actors.Logging; -using PChecker.SystematicTesting.Operations; - -namespace PChecker.Feedback; - - -internal class ConflictOpMonitor: ActorRuntimeLogBase -{ - - public VectorClockGenerator VectorClockGenerator; - - // This dictionary stores all operations received by a machine. - // Each operation is labeled with ActorId, source location, and its corresponding - // vector clock timestamp. - private Dictionary)>> incomingOps = new(); - - private Dictionary> conflictOps = new(); - - public override void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName, Event e, - Guid opGroupId, bool isTargetHalted) - { - var receiverKey = e.Receiver; - var senderKey = e.Sender; - var currentOp = new Operation(senderKey, receiverKey, e.Loc); - if (!incomingOps.ContainsKey(receiverKey)) - { - incomingOps.Add(receiverKey, new HashSet<(Operation, Dictionary)>()); - } - var opsSet = incomingOps[receiverKey]; - - - - if (VectorClockGenerator.ContextVcMap.TryGetValue(senderKey, out var vectorClock)) - { - - foreach (var op in opsSet) - { - if (op.Item1.Sender == currentOp.Sender) - { - continue; - } - - if (!IsLEQ(op.Item2, vectorClock) && !IsLEQ(vectorClock, op.Item2)) - { - AddConflictOp(op.Item1, currentOp); - } - } - opsSet.Add((currentOp, vectorClock - .ToDictionary(entry => entry.Key, entry => entry.Value))); - } - } - - internal bool IsRacing(AsyncOperation op1, AsyncOperation op2) - { - if (op1.Type != AsyncOperationType.Send || op2.Type != AsyncOperationType.Send) { - return false; - } - - var operation1 = new Operation(op1.Name, op1.LastSentReceiver, op1.LastEvent!.Loc); - var operation2 = new Operation(op2.Name, op2.LastSentReceiver, op2.LastEvent!.Loc); - - if (conflictOps.TryGetValue(operation1, out var ops)) { - return ops.Contains(operation2); - } - return false; - } - - public void Reset() { - incomingOps.Clear(); - } - - bool IsLEQ(Dictionary vc1, Dictionary vc2) - { - foreach (var key in vc1.Keys.Union(vc2.Keys)) - { - var op1 = vc1.GetValueOrDefault(key, 0); - var op2 = vc2.GetValueOrDefault(key, 0); - if (op1 > op2) - { - return false; - } - } - return true; - } - - void AddConflictOp(Operation op1, Operation op2) - { - if (!conflictOps.ContainsKey(op1)) { - conflictOps[op1] = new HashSet(); - } - if (!conflictOps.ContainsKey(op2)) { - conflictOps[op2] = new HashSet(); - } - conflictOps[op1].Add(op2); - conflictOps[op2].Add(op1); - } - internal bool IsConflictingOp(AsyncOperation op) - { - if (op.Type != AsyncOperationType.Send) { - return false; - } - - var operation = new Operation(op.Name, op.LastSentReceiver, op.LastEvent!.Loc); - if (conflictOps.TryGetValue(operation, out var ops )) - { - return ops.Count != 0; - } - return false; - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs index d94b042661..b375093a0f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs @@ -9,8 +9,6 @@ public POSScheduleGenerator Mutate(POSScheduleGenerator prev) { return new POSScheduleGenerator(prev.Random, Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), - Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random), - prev.Monitor - ); + Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random)); } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs index fffc0ba15d..b3de2b2db1 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs @@ -13,25 +13,21 @@ internal class POSScheduleGenerator: POSScheduler, IScheduleGenerator PriorityChoices; public RandomChoices SwitchPointChoices; - public ConflictOpMonitor? Monitor; - public POSScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? switchPointChoices, - ConflictOpMonitor? monitor): + public POSScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? switchPointChoices): base(new ParametricProvider( priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), - switchPointChoices != null ? new RandomChoices(switchPointChoices) : new RandomChoices(random)), - monitor) + switchPointChoices != null ? new RandomChoices(switchPointChoices) : new RandomChoices(random))) { Random = random; var provider = (ParametricProvider) Provider; PriorityChoices = provider.PriorityChoices; SwitchPointChoices = provider.SwitchPointChoices; - Monitor = monitor; } - public POSScheduleGenerator(CheckerConfiguration checkerConfiguration, ConflictOpMonitor? monitor): + public POSScheduleGenerator(CheckerConfiguration checkerConfiguration): this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, - null, monitor) + null) { } @@ -42,12 +38,12 @@ public POSScheduleGenerator Mutate() public POSScheduleGenerator New() { - return new POSScheduleGenerator(Random, null, null, Monitor); + return new POSScheduleGenerator(Random, null, null); } public POSScheduleGenerator Copy() { - return new POSScheduleGenerator(Random, PriorityChoices, SwitchPointChoices, Monitor); + return new POSScheduleGenerator(Random, PriorityChoices, SwitchPointChoices); } public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs index 589ae13099..ae44bbec13 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs @@ -15,17 +15,14 @@ internal class POSScheduler: PrioritizedScheduler /// List of prioritized operations. /// private readonly List PrioritizedOperations; - - public ConflictOpMonitor? ConflictOpMonitor; - + /// /// Initializes a new instance of the class. /// - public POSScheduler(PriorizationProvider provider, ConflictOpMonitor? monitor) + public POSScheduler(PriorizationProvider provider) { Provider = provider; PrioritizedOperations = new List(); - ConflictOpMonitor = monitor; } public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) @@ -123,11 +120,6 @@ private AsyncOperation GetPrioritizedOperation(List ops, AsyncOp private bool FindNonRacingOperation(IEnumerable ops, out AsyncOperation next) { var nonRacingOps = ops.Where(op => op.Type != AsyncOperationType.Send); - if (!nonRacingOps.Any() && ConflictOpMonitor != null) - { - nonRacingOps = ops.Where(op => !ConflictOpMonitor.IsConflictingOp(op)); - } - if (!nonRacingOps.Any()) { next = null; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs index 3894b96fc8..ae4eee7531 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs @@ -34,21 +34,19 @@ internal class PriorizationSchedulingBase /// private readonly List PrioritizedOperations; - public ConflictOpMonitor? ConflictOpMonitor; private int _nextPriorityChangePoint; private int _numSwitchPointsLeft; /// /// Initializes a new instance of the class. /// - public PriorizationSchedulingBase(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider, ConflictOpMonitor? monitor) + public PriorizationSchedulingBase(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider) { Provider = provider; ScheduledSteps = 0; ScheduleLength = scheduleLength; MaxPrioritySwitchPoints = maxPrioritySwitchPoints; PrioritizedOperations = new List(); - ConflictOpMonitor = monitor; _numSwitchPointsLeft = maxPrioritySwitchPoints; double switchPointProbability = 0.1; @@ -66,7 +64,7 @@ public virtual bool GetNextOperation(AsyncOperation current, IEnumerable op.Status is AsyncOperationStatus.Enabled).ToList(); if (enabledOperations.Count == 0) { - if (ConflictOpMonitor == null && _nextPriorityChangePoint == ScheduledSteps) + if (_nextPriorityChangePoint == ScheduledSteps) { MovePriorityChangePointForward(); } @@ -78,25 +76,9 @@ public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops) - { - foreach (var op in ops) - { - if (op != next && ConflictOpMonitor.IsRacing(next, op)) - { - PrioritizedOperations.Remove(op); - } - } - PrioritizedOperations.Remove(next); - } private void MovePriorityChangePointForward() { @@ -137,13 +119,8 @@ private AsyncOperation GetPrioritizedOperation(List ops, AsyncOp Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); } - if (ConflictOpMonitor != null && FindNonRacingOperation(ops, out var next)) - { - return next; - } - var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); - if (ConflictOpMonitor == null && _nextPriorityChangePoint == ScheduledSteps) + if (_nextPriorityChangePoint == ScheduledSteps) { if (ops.Count == 1) { @@ -190,32 +167,6 @@ private AsyncOperation GetPrioritizedOperation(List ops, AsyncOp return ops.First(op => op.Equals(prioritizedSchedulable)); } - private bool FindNonRacingOperation(IEnumerable ops, out AsyncOperation next) - { - var nonRacingOps = ops.Where(op => op.Type != AsyncOperationType.Send); - if (!nonRacingOps.Any()) - { - var sendOps = ops.Where(op => op.Type == AsyncOperationType.Send); - nonRacingOps = ops.Where(op => !ConflictOpMonitor.IsConflictingOp(op)); - } - - if (!nonRacingOps.Any()) - { - next = null; - return false; - } - else if (!nonRacingOps.Skip(1).Any()) - { - next = nonRacingOps.First(); - return true; - } - else - { - next = GetHighestPriorityEnabledOperation(nonRacingOps); - return true; - } - - } public void Reset() { diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index d226141c10..d15d8316d4 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -65,11 +65,6 @@ public class TestingEngine /// internal readonly ISchedulingStrategy Strategy; - /// - /// Monitors conflict operations used by the POS Strategy. - /// - private ConflictOpMonitor? _conflictOpObserver; - /// /// Random value generator used by the scheduling strategies. /// @@ -303,12 +298,6 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo { JsonVerboseLogs = new List>(); } - - if (checkerConfiguration.EnableConflictAnalysis) - { - _conflictOpObserver = new ConflictOpMonitor(); - } - if (checkerConfiguration.SchedulingStrategy is "replay") { var scheduleDump = GetScheduleForReplay(out var isFair); @@ -328,8 +317,7 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "pos") { - var scheduler = new POSScheduler(new RandomPriorizationProvider(RandomValueGenerator), - _conflictOpObserver); + var scheduler = new POSScheduler(new RandomPriorizationProvider(RandomValueGenerator)); Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, scheduler); } @@ -372,7 +360,7 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo Strategy = new FeedbackGuidedStrategy( _checkerConfiguration, new RandomInputGenerator(checkerConfiguration), - new POSScheduleGenerator(_checkerConfiguration, _conflictOpObserver)); + new POSScheduleGenerator(_checkerConfiguration)); } else if (checkerConfiguration.SchedulingStrategy is "portfolio") { @@ -568,12 +556,6 @@ private void RegisterObservers(ControlledRuntime runtime) // Always output a json log of the error JsonLogger = new JsonWriter(); runtime.SetJsonLogger(JsonLogger); - - if (_conflictOpObserver != null) - { - _conflictOpObserver.VectorClockGenerator = JsonLogger.VcGenerator; - runtime.RegisterLog(_conflictOpObserver); - } } /// @@ -716,7 +698,6 @@ private void RunNextIteration(int schedule) runtimeLogger?.Dispose(); runtime?.Dispose(); - _conflictOpObserver?.Reset(); } } diff --git a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs index 407321454d..6b62e9f69d 100644 --- a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs +++ b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs @@ -81,7 +81,6 @@ internal PCheckerOptions() advancedGroup.AddArgument("xml-trace", null, "Specify a filename for XML runtime log output to be written to", typeof(bool)); advancedGroup.AddArgument("psym-args", null, "Specify a concatenated list of additional PSym-specific arguments to pass, each starting with a colon").IsHidden = true; advancedGroup.AddArgument("jvm-args", null, "Specify a concatenated list of PSym-specific JVM arguments to pass, each starting with a colon").IsHidden = true; - advancedGroup.AddArgument("conflict-analysis", null, "Enable POS conflict analysis.", typeof(bool)); } /// @@ -327,9 +326,6 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c case "jvm-args": checkerConfiguration.JvmArgs = ((string)option.Value).Replace(':', ' '); break; - case "conflict-analysis": - checkerConfiguration.EnableConflictAnalysis = true; - break; case "pproj": // do nothing, since already configured through UpdateConfigurationWithPProjectFile break; From ccaa27674abf5d44d1e07ea55d6a96be8438ccd2 Mon Sep 17 00:00:00 2001 From: ChristineZh0u <48167738+ChristineZh0u@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:08:59 -0700 Subject: [PATCH 08/21] Removing compiler changes (#789) Co-authored-by: Christine Zhou --- .../CheckerCore/Actors/Events/Event.cs | 1 - Src/PChecker/CheckerCore/PRuntime/PEvent.cs | 8 +- Src/PChecker/CheckerCore/PRuntime/PMachine.cs | 8 +- .../Backend/CSharp/CSharpCodeGenerator.cs | 105 +++--------------- .../CompilerCore/Backend/IRTransformer.cs | 3 - Src/PCompiler/CompilerCore/Parser/PLexer.g4 | 1 - Src/PCompiler/CompilerCore/Parser/PParser.g4 | 8 -- .../TypeChecker/AST/Declarations/Function.cs | 6 +- .../AST/Declarations/FunctionSignature.cs | 2 - .../AST/Statements/ConstraintStmt.cs | 15 --- .../CompilerCore/TypeChecker/Analyzer.cs | 9 +- .../ConstraintVariableCollector.cs | 33 ------ .../TypeChecker/ControlFlowChecker.cs | 1 - .../TypeChecker/DeclarationStubVisitor.cs | 20 ---- .../TypeChecker/DeclarationVisitor.cs | 26 +---- .../CompilerCore/TypeChecker/ExprVisitor.cs | 23 +--- .../TypeChecker/FunctionBodyVisitor.cs | 27 ----- .../TypeChecker/FunctionValidator.cs | 2 +- .../TypeChecker/ScenarioEventVisitor.cs | 20 ---- .../CompilerCore/TypeChecker/Scope.cs | 8 -- .../CompilerCore/TypeChecker/TypeResolver.cs | 2 +- .../TypeChecker/Types/NamedTupleEntry.cs | 11 -- 22 files changed, 32 insertions(+), 307 deletions(-) delete mode 100644 Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs delete mode 100644 Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs delete mode 100644 Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs diff --git a/Src/PChecker/CheckerCore/Actors/Events/Event.cs b/Src/PChecker/CheckerCore/Actors/Events/Event.cs index ca9fe6b4d5..57917c052b 100644 --- a/Src/PChecker/CheckerCore/Actors/Events/Event.cs +++ b/Src/PChecker/CheckerCore/Actors/Events/Event.cs @@ -11,7 +11,6 @@ namespace PChecker.Actors.Events [DataContract] public abstract class Event { - public int Loc { get; set; } public string? Sender; public string? Receiver; public string? State; diff --git a/Src/PChecker/CheckerCore/PRuntime/PEvent.cs b/Src/PChecker/CheckerCore/PRuntime/PEvent.cs index 8059cd490d..33af7d6790 100644 --- a/Src/PChecker/CheckerCore/PRuntime/PEvent.cs +++ b/Src/PChecker/CheckerCore/PRuntime/PEvent.cs @@ -6,15 +6,13 @@ namespace PChecker.PRuntime { public class PEvent : Event, IPrtValue { - public PEvent(int loc) : base() + public PEvent() : base() { - Loc = loc; } - public PEvent(IPrtValue payload, int loc) : base() + public PEvent(IPrtValue payload) : base() { Payload = payload; - Loc = loc; } public IPrtValue Payload { get; } @@ -52,7 +50,7 @@ public override string ToString() public class PHalt : PEvent { - public PHalt(IPrtValue payload, int loc) : base(payload, loc) + public PHalt(IPrtValue payload) : base(payload) { } } diff --git a/Src/PChecker/CheckerCore/PRuntime/PMachine.cs b/Src/PChecker/CheckerCore/PRuntime/PMachine.cs index d23ef2c457..d95b2e38de 100644 --- a/Src/PChecker/CheckerCore/PRuntime/PMachine.cs +++ b/Src/PChecker/CheckerCore/PRuntime/PMachine.cs @@ -84,7 +84,7 @@ public void TrySendEvent(PMachineValue target, Event ev, object payload = null) Assert(target.Permissions.Contains(ev.GetType().Name), $"Event {ev.GetType().Name} is not in the permissions set of the target machine"); var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); - ev = (Event)oneArgConstructor.Invoke(new[] { payload , ev.Loc}); + ev = (Event)oneArgConstructor.Invoke(new[] { payload }); ev.Sender = Id.ToString(); ev.Receiver = target.Id.ToString(); @@ -96,7 +96,7 @@ public void TryRaiseEvent(Event ev, object payload = null) { Assert(ev != null, "Machine cannot raise a null event"); var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); - ev = (Event)oneArgConstructor.Invoke(new[] { payload, ev.Loc }); + ev = (Event)oneArgConstructor.Invoke(new[] { payload }); RaiseEvent(ev); throw new PNonStandardReturnException { ReturnKind = NonStandardReturn.Raise }; } @@ -190,7 +190,7 @@ public void Announce(Event ev, object payload = null) } var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); - var @event = (Event)oneArgConstructor.Invoke(new[] { payload, ev.Loc }); + var @event = (Event)oneArgConstructor.Invoke(new[] { payload }); var pText = payload == null ? "" : $" with payload {((IPrtValue)payload).ToEscapedString()}"; Logger.WriteLine($" '{Id}' announced event '{ev.GetType().Name}'{pText}."); @@ -255,7 +255,7 @@ public object ToDict() public class InitializeParametersEvent : PEvent { - public InitializeParametersEvent(InitializeParameters payload) : base(payload, 0) + public InitializeParametersEvent(InitializeParameters payload) : base(payload) { } } diff --git a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs index 07a7d17d74..c7a2223bc3 100644 --- a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs +++ b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs @@ -22,8 +22,6 @@ public class CSharpCodeGenerator : ICodeGenerator /// public bool HasCompilationStage => true; - private int _sendEventIndex = 0; - public void Compile(ICompilerConfiguration job) { var csprojName = $"{job.ProjectName}.csproj"; @@ -486,8 +484,8 @@ private void WriteEvent(CompilationContext context, StringWriter output, PEvent var payloadType = GetCSharpType(pEvent.PayloadType, true); context.WriteLine(output, $"internal partial class {declName} : PEvent"); context.WriteLine(output, "{"); - context.WriteLine(output, $"public {declName}() : base(-1) {{}}"); - context.WriteLine(output, $"public {declName}({payloadType} payload, int loc): base(payload, loc)" + "{ }"); + context.WriteLine(output, $"public {declName}() : base() {{}}"); + context.WriteLine(output, $"public {declName} ({payloadType} payload): base(payload)" + "{ }"); context.WriteLine(output, $"public override IPrtValue Clone() {{ return new {declName}();}}"); context.WriteLine(output, "}"); @@ -512,12 +510,12 @@ private void WriteMachine(CompilationContext context, StringWriter output, Machi var cTorType = GetCSharpType(machine.PayloadType, true); context.Write(output, "public class ConstructorEvent : PEvent"); context.Write(output, "{"); - context.Write(output, $"public ConstructorEvent({cTorType} val, int loc) : base(val, loc) {{ }}"); + context.Write(output, $"public ConstructorEvent({cTorType} val) : base(val) {{ }}"); context.WriteLine(output, "}"); context.WriteLine(output); context.WriteLine(output, - $"protected override Event GetConstructorEvent(IPrtValue value) {{ return new ConstructorEvent(({cTorType})value, {_sendEventIndex++}); }}"); + $"protected override Event GetConstructorEvent(IPrtValue value) {{ return new ConstructorEvent(({cTorType})value); }}"); // create the constructor to initialize the sends, creates and receives list WriteMachineConstructor(context, output, machine); @@ -708,7 +706,7 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func var staticKeyword = isStatic ? "static " : ""; var asyncKeyword = isAsync ? "async " : ""; - var returnType = function.Role != FunctionRole.Scenario ? GetCSharpType(signature.ReturnType) : "int"; + var returnType = GetCSharpType(signature.ReturnType); if (isAsync) { @@ -721,10 +719,6 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func { functionParameters = "Event currentMachine_dequeuedEvent"; } - else if (function.Role == FunctionRole.Scenario) - { - functionParameters = "List events"; - } else { functionParameters = string.Join( @@ -733,7 +727,7 @@ private void WriteFunction(CompilationContext context, StringWriter output, Func $"{GetCSharpType(param.Type)} {context.Names.GetNameForDecl(param)}")); } - if (isStatic && function.Role != FunctionRole.Scenario) // then we need to generate two versions of the function + if (isStatic) // then we need to generate two versions of the function { // for machine var seperator = functionParameters == "" ? "" : ", "; @@ -789,69 +783,14 @@ private void WriteFunctionBody(CompilationContext context, StringWriter output, $"{GetCSharpType(type, true)} {context.Names.GetNameForDecl(local)} = {GetDefaultValue(type)};"); } - if (function.Role != FunctionRole.Scenario) - { - foreach (var bodyStatement in function.Body.Statements) - { - WriteStmt(context: context, output: output, function: function, stmt: bodyStatement); - } - } - else - { - WriteScenario(context, output, function); - } - - - context.WriteLine(output, "}"); - } - - private void WriteScenario(CompilationContext context, StringWriter output, Function function) - { - int numOfStmt = function.Body.Statements.Count + 1; - context.WriteLine(output, $"int state = {numOfStmt};"); - var eventPredicates = string.Join(" or ", function.Signature.ParameterEvents.Select(it => it.Name)); - context.WriteLine(output, $"events = events.Where(it => it is {eventPredicates}).ToList();"); - WriteConstraintsRecursive(context, output, function, 0, new HashSet(), 0); - context.WriteLine(output, "return state;"); - } - - private void WriteConstraintsRecursive(CompilationContext context, StringWriter output, Function function, int index, HashSet visitedVariables, int satisfiedConstraints) - { - if (index >= function.Signature.Parameters.Count) - { - context.WriteLine(output, "return 1;"); - return; - } - var param = function.Signature.Parameters[index]; - var e = function.Signature.ParameterEvents[index]; - visitedVariables.Add(param); - var start = index == 0 ? "0" : $"i{index - 1} + 1"; - var paramName = context.Names.GetNameForDecl(param); - context.WriteLine(output, $"for (var i{index} = {start} ; i{index} < events.Count; i{index} ++) " + "{"); - context.WriteLine(output, $"var {paramName}_obj = events[i{index}];"); - context.WriteLine(output, $"if ({paramName}_obj is not {e.Name}) continue;"); - context.WriteLine(output, $"var {paramName} = ((PEvent) {paramName}_obj).Payload;"); - foreach (var bodyStatement in function.Body.Statements) { - if (bodyStatement is ConstraintStmt stmt) - { - var variables = ConstraintVariableCollector.FindVariablesRecursive(stmt.Constraint); - if (variables.Contains(param) && visitedVariables.IsSupersetOf(variables)) - { - context.Write(output, $"if (!("); - WriteExpr(context, output, stmt.Constraint); - context.WriteLine(output, $")) continue;"); - satisfiedConstraints += 1; - context.WriteLine(output, $"state = Math.Min({function.Body.Statements.Count - satisfiedConstraints + 1}, state);"); - } - } + WriteStmt(context: context, output: output, function: function, stmt: bodyStatement); } - WriteConstraintsRecursive(context, output, function, index + 1, visitedVariables, satisfiedConstraints); + context.WriteLine(output, "}"); } - private void WriteStmt(CompilationContext context, StringWriter output, Function function, IPStmt stmt) { switch (stmt) @@ -1240,19 +1179,9 @@ private void WriteLValue(CompilationContext context, StringWriter output, IPExpr break; case NamedTupleAccessExpr namedTupleAccessExpr: - if (ExprVisitor.ReservedEventFeilds.ContainsValue(namedTupleAccessExpr.Entry)) - { - var type = GetCSharpType(namedTupleAccessExpr.Entry.Type); - context.Write(output, $"(({type}) ("); - WriteExpr(context, output, namedTupleAccessExpr.SubExpr); - context.Write(output, $"_obj).{namedTupleAccessExpr.FieldName})"); - } - else - { - context.Write(output, "((PrtNamedTuple)"); - WriteExpr(context, output, namedTupleAccessExpr.SubExpr); - context.Write(output, $")[\"{namedTupleAccessExpr.FieldName}\"]"); - } + context.Write(output, "((PrtNamedTuple)"); + WriteExpr(context, output, namedTupleAccessExpr.SubExpr); + context.Write(output, $")[\"{namedTupleAccessExpr.FieldName}\"]"); break; case SeqAccessExpr seqAccessExpr: @@ -1296,9 +1225,9 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p context.Write(output, $"({negate}PrtValues.SafeEquals("); if (PLanguageType.TypeIsOfKind(binOpExpr.Lhs.Type, TypeKind.Enum)) { - context.Write(output, "PrtValues.Box((long) ((PrtInt)"); + context.Write(output, "PrtValues.Box((long) "); WriteExpr(context, output, binOpExpr.Lhs); - context.Write(output, ")),"); + context.Write(output, "),"); } else { @@ -1308,9 +1237,9 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p if (PLanguageType.TypeIsOfKind(binOpExpr.Rhs.Type, TypeKind.Enum)) { - context.Write(output, "PrtValues.Box((long) ((PrtInt)"); + context.Write(output, "PrtValues.Box((long) "); WriteExpr(context, output, binOpExpr.Rhs); - context.Write(output, "))"); + context.Write(output, ")"); } else { @@ -1459,7 +1388,7 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p switch (eventName) { case "Halt": - context.Write(output, "new PHalt(" + _sendEventIndex++ + ")"); + context.Write(output, "new PHalt()"); break; case "DefaultEvent": @@ -1468,7 +1397,7 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p default: var payloadExpr = GetDefaultValue(eventRefExpr.Value.PayloadType); - context.Write(output, $"new {eventName}({payloadExpr}, {_sendEventIndex++})"); + context.Write(output, $"new {eventName}({payloadExpr})"); break; } diff --git a/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs b/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs index 8e6992cf9b..943eba0df8 100644 --- a/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs +++ b/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs @@ -567,9 +567,6 @@ private List SimplifyStatement(IPStmt statement) condCheck.Concat(SimplifyStatement(whileStmt.Body))); return new List { new WhileStmt(location, new BoolLiteralExpr(location, true), loopBody) }; - // We do not rewrite constraint statements for now. - case ConstraintStmt constraintStmt: - return new List() { constraintStmt }; default: throw new ArgumentOutOfRangeException(nameof(statement)); diff --git a/Src/PCompiler/CompilerCore/Parser/PLexer.g4 b/Src/PCompiler/CompilerCore/Parser/PLexer.g4 index e8fb715019..8f7511ac8d 100644 --- a/Src/PCompiler/CompilerCore/Parser/PLexer.g4 +++ b/Src/PCompiler/CompilerCore/Parser/PLexer.g4 @@ -70,7 +70,6 @@ MODULE : 'module' ; IMPLEMENTATION : 'implementation' ; TEST : 'test' ; REFINES : 'refines' ; -SCENARIO : 'scenario' ; // module constructors COMPOSE : 'compose' ; diff --git a/Src/PCompiler/CompilerCore/Parser/PParser.g4 b/Src/PCompiler/CompilerCore/Parser/PParser.g4 index 3140b37edb..dc00fca475 100644 --- a/Src/PCompiler/CompilerCore/Parser/PParser.g4 +++ b/Src/PCompiler/CompilerCore/Parser/PParser.g4 @@ -60,7 +60,6 @@ topDecl : typeDefDecl | namedModuleDecl | testDecl | implementationDecl - | scenarioDecl ; @@ -108,13 +107,6 @@ funDecl : FUN name=iden LPAREN funParamList? RPAREN (COLON type)? (CREATES inter | FUN name=iden LPAREN funParamList? RPAREN (COLON type)? functionBody # PFunDecl ; -scenarioDecl : SCENARIO scenarioName=iden LPAREN scenarioParamList RPAREN scenarioBody; - -scenarioParamList: scenarioParam (COMMA scenarioParam)*; -scenarioParam : name=iden COLON nonDefaultEvent; -scenarioBody : LBRACE expr (COMMA expr)* RBRACE; - - stateDecl : START? temperature=(HOT | COLD)? STATE name=iden LBRACE stateBodyItem* RBRACE ; stateBodyItem : ENTRY anonEventHandler # StateEntry diff --git a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs index c5001cbca9..69ed97c4ad 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Function.cs @@ -15,8 +15,7 @@ public enum FunctionRole EventHandler = 1 << 4, ExitHandler = 1 << 5, ReceiveHandler = 1 << 6, - Foreign = 1 << 7, - Scenario = 1 << 8, + Foreign = 1 << 7 } public class Function : IPDecl, IHasScope @@ -33,8 +32,7 @@ sourceNode is PParser.AnonEventHandlerContext || sourceNode is PParser.NoParamAnonEventHandlerContext || sourceNode is PParser.ReceiveStmtContext || sourceNode is PParser.WhileStmtContext || - sourceNode is PParser.ForeachStmtContext || - sourceNode is PParser.ScenarioDeclContext); + sourceNode is PParser.ForeachStmtContext); Name = name; SourceLocation = sourceNode; CanCreate = false; diff --git a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs index c3801faab7..b711938af6 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/FunctionSignature.cs @@ -7,8 +7,6 @@ namespace Plang.Compiler.TypeChecker.AST.Declarations public class FunctionSignature { public List Parameters { get; } = new List(); - // This is only used by scenarios. - public List ParameterEvents { get; } = new(); public IEnumerable ParameterTypes => Parameters.Select(ty => ty.Type); public PLanguageType ReturnType { get; set; } = PrimitiveType.Null; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs b/Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs deleted file mode 100644 index 163e1a5953..0000000000 --- a/Src/PCompiler/CompilerCore/TypeChecker/AST/Statements/ConstraintStmt.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Antlr4.Runtime; - -namespace Plang.Compiler.TypeChecker.AST.Statements; - -public class ConstraintStmt : IPStmt -{ - public ConstraintStmt(ParserRuleContext sourceLocation, IPExpr constraint) - { - SourceLocation = sourceLocation; - Constraint = constraint; - } - - public IPExpr Constraint { get; } - public ParserRuleContext SourceLocation { get; } -} \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs b/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs index b395a56024..3139ff3f93 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Analyzer.cs @@ -179,14 +179,7 @@ private static Scope BuildGlobalScope(ICompilerConfiguration config, PParser.Pro { DeclarationVisitor.PopulateDeclarations(config.Handler, globalScope, programUnit, nodesToDeclarations); } - - // Step 3: Assign param types for scenario events. We have do this after all events are initialized. - foreach (var function in globalScope.Functions) - { - ScenarioEventVisitor.PopulateEventTypes(function); - } - - + return globalScope; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs b/Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs deleted file mode 100644 index 647d9c1168..0000000000 --- a/Src/PCompiler/CompilerCore/TypeChecker/ConstraintVariableCollector.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Plang.Compiler.TypeChecker.AST; -using Plang.Compiler.TypeChecker.AST.Declarations; -using Plang.Compiler.TypeChecker.AST.Expressions; - -namespace Plang.Compiler.TypeChecker; - -public class ConstraintVariableCollector -{ - - public static HashSet FindVariablesRecursive(IPExpr expr) - { - switch (expr) - { - case BinOpExpr binOp: - return FindVariablesRecursive(binOp.Lhs).Union(FindVariablesRecursive(binOp.Rhs)).ToHashSet(); - case NamedTupleAccessExpr namedTupleAccessExpr: - return FindVariablesRecursive(namedTupleAccessExpr.SubExpr); - case VariableAccessExpr variableAccessExpr: - return new HashSet() { variableAccessExpr.Variable }; - case EnumElemRefExpr: - case StringExpr: - case IntLiteralExpr: - break; - default: - throw new ArgumentOutOfRangeException(nameof(expr)); - } - - return new HashSet(); - } -} \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs b/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs index bddcbbf892..ea09002934 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/ControlFlowChecker.cs @@ -84,7 +84,6 @@ private void CheckStmt(IPStmt stmt) case RemoveStmt _: case ReturnStmt _: case SendStmt _: - case ConstraintStmt _: break; default: diff --git a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs index c2bf47b518..77782e7308 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationStubVisitor.cs @@ -163,26 +163,6 @@ public override object VisitStateDecl(PParser.StateDeclContext context) #endregion Machines - #region Scenario - - public override object VisitScenarioDecl(PParser.ScenarioDeclContext context) - { - var scenarioName = context.scenarioName.GetText(); - var scenario = CurrentScope.Put(scenarioName, context); - nodesToDeclarations.Put(context, scenario); - return VisitChildrenWithNewScope(scenario, context); - } - - public override object VisitScenarioParam(PParser.ScenarioParamContext context) - { - var symbolName = context.name.GetText(); - var decl = CurrentScope.Put(symbolName, context, VariableRole.Param); - nodesToDeclarations.Put(context, decl); - return null; - } - - #endregion - #region Functions public override object VisitPFunDecl(PParser.PFunDeclContext context) diff --git a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs index 0c02c0a2a9..82faffc717 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/DeclarationVisitor.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using Antlr4.Runtime.Tree; using Plang.Compiler.TypeChecker.AST; using Plang.Compiler.TypeChecker.AST.Declarations; @@ -349,30 +348,7 @@ public override object VisitSpecMachineDecl(PParser.SpecMachineDeclContext conte return specMachine; } - - public override object VisitScenarioDecl(PParser.ScenarioDeclContext context) - { - var scenario = (Function)nodesToDeclarations.Get(context); - var result = ((Variable, PEvent)[]) Visit(context.scenarioParamList()); - scenario.Signature.Parameters.AddRange(result.Select(it => it.Item1)); - scenario.Signature.ParameterEvents.AddRange(result.Select(it => it.Item2)); - scenario.Signature.ReturnType = PrimitiveType.Int; - scenario.Role = FunctionRole.Scenario; - return scenario; - } - - public override object VisitScenarioParamList(PParser.ScenarioParamListContext context) - { - return context.scenarioParam().Select(Visit).Cast<(Variable, PEvent)>().ToArray(); - } - - public override object VisitScenarioParam(PParser.ScenarioParamContext context) - { - var param = (Variable) nodesToDeclarations.Get(context); - var e = (PEvent) Visit(context.nonDefaultEvent()); - return (param, e); - } - + public override object VisitMachineBody(PParser.MachineBodyContext context) { foreach (var machineEntryContext in context.machineEntry()) diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs index 3b5c325c78..7a85bf6e84 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/ExprVisitor.cs @@ -12,18 +12,6 @@ namespace Plang.Compiler.TypeChecker { public class ExprVisitor : PParserBaseVisitor { - public static readonly string EventFieldSender = "MSG_sender"; - public static readonly string EventFieldReceiver = "MSG_receiver"; - public static readonly string EventFieldState = "MSG_state"; - public static readonly string EventFieldIndex = "MSG_index"; - - public static Dictionary ReservedEventFeilds = new Dictionary() - { - { EventFieldSender, new NamedTupleEntry("Sender", -1, PrimitiveType.String) }, - { EventFieldReceiver, new NamedTupleEntry("Receiver", -1, PrimitiveType.String) }, - { EventFieldState, new NamedTupleEntry("State", -1, PrimitiveType.String) }, - { EventFieldIndex, new NamedTupleEntry("Index", -1, PrimitiveType.Int) }, - }; private readonly ITranslationErrorHandler handler; private readonly Function method; private readonly Scope table; @@ -58,18 +46,11 @@ public override IPExpr VisitParenExpr(PParser.ParenExprContext context) public override IPExpr VisitNamedTupleAccessExpr(PParser.NamedTupleAccessExprContext context) { var subExpr = Visit(context.expr()); - var fieldName = context.field.GetText(); - if (subExpr is VariableAccessExpr v && method.Role == FunctionRole.Scenario && method.Signature.Parameters.Contains(v.Variable)) - { - if (ReservedEventFeilds.TryGetValue(fieldName, out var reservedEntry)) - { - return new NamedTupleAccessExpr(context, subExpr, reservedEntry); - } - } if (!(subExpr.Type.Canonicalize() is NamedTupleType tuple)) { throw handler.TypeMismatch(subExpr, TypeKind.NamedTuple); } + var fieldName = context.field.GetText(); if (!tuple.LookupEntry(fieldName, out var entry)) { throw handler.MissingNamedTupleEntry(context.field, tuple); @@ -627,7 +608,7 @@ public override IPExpr VisitNamedTupleBody(PParser.NamedTupleBodyContext context } names.Add(entryName); - entries[i] = new NamedTupleEntry(name: entryName, fieldNo: i, type: fields[i].Type); + entries[i] = new NamedTupleEntry { Name = entryName, FieldNo = i, Type = fields[i].Type }; } var type = new NamedTupleType(entries); diff --git a/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs index b3572fb73f..2cb0b21eb3 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/FunctionBodyVisitor.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; using System.Diagnostics.Contracts; -using Plang.Compiler.TypeChecker.AST; using Plang.Compiler.TypeChecker.AST.Declarations; using Plang.Compiler.TypeChecker.AST.Statements; -using Plang.Compiler.TypeChecker.Types; namespace Plang.Compiler.TypeChecker { @@ -63,30 +60,6 @@ public override object VisitFunctionBody(PParser.FunctionBodyContext context) return null; } - public override object VisitScenarioDecl(PParser.ScenarioDeclContext context) - { - return Visit(context.scenarioBody()); - } - - public override object VisitScenarioBody(PParser.ScenarioBodyContext context) - { - var exprVisitor = new ExprVisitor(method, config.Handler); - // var compound = new CompoundStmt(context, new IPStmt[0]); - var stmts = new List(); - foreach (var exprContext in context.expr()) - { - var constraint = exprVisitor.Visit(exprContext); - if (!Equals(constraint.Type, PrimitiveType.Bool)) - { - throw config.Handler.TypeMismatch(exprContext, constraint.Type, PrimitiveType.Bool); - } - stmts.Add( new ConstraintStmt(exprContext, constraint)); - } - - method.Body = new CompoundStmt(context, stmts); - return null; - } - public override object VisitVarDecl(PParser.VarDeclContext context) { foreach (var varName in context.idenList()._names) diff --git a/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs b/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs index 35bd8a3be4..16b1204a1a 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/FunctionValidator.cs @@ -11,7 +11,7 @@ public static class FunctionValidator { public static void CheckAllPathsReturn(ITranslationErrorHandler handler, Function function) { - if (function.IsForeign || function.Role == FunctionRole.Scenario) + if (function.IsForeign) { return; } diff --git a/Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs b/Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs deleted file mode 100644 index 3cd4518397..0000000000 --- a/Src/PCompiler/CompilerCore/TypeChecker/ScenarioEventVisitor.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Antlr4.Runtime.Tree; -using Plang.Compiler.TypeChecker.AST; -using Plang.Compiler.TypeChecker.AST.Declarations; - -namespace Plang.Compiler.TypeChecker; - -public class ScenarioEventVisitor -{ - - public static void PopulateEventTypes(Function function) - { - if (function.Role != FunctionRole.Scenario) return; - for (var i = 0; i < function.Signature.Parameters.Count; i++) - { - var v = function.Signature.Parameters[i]; - var e = function.Signature.ParameterEvents[i]; - v.Type = e.PayloadType; - } - } -} \ No newline at end of file diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs b/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs index 22dc0b4bc8..9eff2b90ce 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Scope.cs @@ -518,14 +518,6 @@ public Interface Put(string name, PParser.InterfaceDeclContext tree) return machineInterface; } - public Function Put(string name, PParser.ScenarioDeclContext tree) - { - var scenario = new Function(name, tree); - CheckConflicts(scenario, Namespace(functions)); - functions.Add(name, scenario); - return scenario; - } - public Machine Put(string name, PParser.ImplMachineDeclContext tree) { var machine = new Machine(name, tree); diff --git a/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs b/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs index 3fb843d8d9..39b22c3681 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/TypeResolver.cs @@ -121,7 +121,7 @@ public override PLanguageType VisitNamedTupleType(PParser.NamedTupleTypeContext } names.Add(fieldName); - fields[i] = new NamedTupleEntry(name: fieldName, fieldNo: i, type: Visit(field.type())); + fields[i] = new NamedTupleEntry { Name = fieldName, FieldNo = i, Type = Visit(field.type()) }; } var ret = new NamedTupleType(fields); diff --git a/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs b/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs index 89ad48ae17..f9223f79c4 100644 --- a/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs +++ b/Src/PCompiler/CompilerCore/TypeChecker/Types/NamedTupleEntry.cs @@ -2,17 +2,6 @@ namespace Plang.Compiler.TypeChecker.Types { public class NamedTupleEntry { - public NamedTupleEntry() - { - } - - public NamedTupleEntry(string name, int fieldNo, PLanguageType type) - { - Name = name; - FieldNo = fieldNo; - Type = type; - } - public string Name { get; set; } public int FieldNo { get; set; } public PLanguageType Type { get; set; } From 61fa8d325213b93234fca577c886411636e9dead Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 10 Oct 2024 16:00:52 +0800 Subject: [PATCH 09/21] Cleanup. --- .../SystematicTesting/ControlledRuntime.cs | 1 - .../SystematicTesting/OperationScheduler.cs | 4 +- .../Operations/AsyncOperation.cs | 5 ++- .../SystematicTesting/Strategies/Utils.cs | 44 ------------------- .../SystematicTesting/TestingEngine.cs | 13 ++---- .../PCommandLine/Options/PCheckerOptions.cs | 5 --- 6 files changed, 9 insertions(+), 63 deletions(-) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs index 2ed17adb40..ee4f21507e 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs @@ -459,7 +459,6 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Guid "Cannot send event '{0}' to actor id '{1}' that is not bound to an actor instance.", e.GetType().FullName, targetId.Value); - Scheduler.ScheduledOperation.LastEvent = e; Scheduler.ScheduledOperation.LastSentReceiver = targetId.ToString(); Scheduler.ScheduleNextEnabledOperation(AsyncOperationType.Send); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs index a148bd4586..e21e6770e2 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/OperationScheduler.cs @@ -150,7 +150,7 @@ internal void ScheduleNextEnabledOperation(AsyncOperationType type) } // Get and order the operations by their id. - var ops = OperationMap.Values.OrderBy(op => op.Id).ToList(); + var ops = OperationMap.Values.OrderBy(op => op.Id); // Try enable any operation that is currently waiting, but has its dependencies already satisfied. foreach (var op in ops) @@ -162,8 +162,6 @@ internal void ScheduleNextEnabledOperation(AsyncOperationType type) } } - // ops = Utils.FindHighPriorityOperations(ops, CheckerConfiguration.InterestingEvents); - if (!Strategy.GetNextOperation(current, ops, out var next)) { // Checks if the program has deadlocked. diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs index a94337e3a8..46d7112852 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs @@ -50,7 +50,10 @@ internal abstract class AsyncOperation : IAsyncOperation /// True if the next awaiter is controlled, else false. /// internal bool IsAwaiterControlled; - public Event? LastEvent = null; + + /// + /// The receiver if the operation is Send. + /// public string LastSentReceiver = ""; /// diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs deleted file mode 100644 index d4b3a8a0c1..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Utils.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PChecker.SystematicTesting.Operations; - -namespace PChecker.SystematicTesting.Strategies; - -public class Utils -{ - internal static List FindHighPriorityOperations(IEnumerable ops, HashSet interestingEvents) - { - var highOps = ops.Where(it => - - { - if (it.Status == AsyncOperationStatus.Enabled) - { - if (it is ActorOperation act) - { - if (act.Type == AsyncOperationType.Send) - { - if (act.LastEvent != null) - { - return !interestingEvents.Contains(act.LastEvent.GetType()); - } - return false; - } - } - return true; - } - return false; - } - ).ToList(); - if (highOps.Count != 0) - { - return highOps; - } - return ops.Where( - op => - { - return op.Status is AsyncOperationStatus.Enabled; - } - ).ToList(); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index d15d8316d4..4ada617a5a 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -179,7 +179,6 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration) => Create(checkerConfiguration, LoadAssembly(checkerConfiguration.AssemblyToBeAnalyzed)); private Stopwatch watch; - private bool ShouldEmitTrace; /// /// Creates a new systematic testing engine. @@ -211,13 +210,10 @@ public static TestingEngine Create(CheckerConfiguration checkerConfiguration, As { testMethodInfo = TestMethodInfo.GetFromAssembly(assembly, checkerConfiguration.TestCaseName); Console.Out.WriteLine($".. Test case :: {testMethodInfo.Name}"); - - Type t = assembly.GetType("PImplementation.GlobalFunctions"); } catch { - Error.ReportAndExit( - $"Failed to get test method '{checkerConfiguration.TestCaseName}' from assembly '{assembly.FullName}'"); + Error.ReportAndExit($"Failed to get test method '{checkerConfiguration.TestCaseName}' from assembly '{assembly.FullName}'"); } return new TestingEngine(checkerConfiguration, testMethodInfo); @@ -449,7 +445,7 @@ private System.Threading.Tasks.Task CreateTestingTask() var options = string.Empty; if (_checkerConfiguration.SchedulingStrategy is "random" || _checkerConfiguration.SchedulingStrategy is "pct" || - _checkerConfiguration.SchedulingStrategy is "poc" || + _checkerConfiguration.SchedulingStrategy is "pos" || _checkerConfiguration.SchedulingStrategy is "feedbackpct" || _checkerConfiguration.SchedulingStrategy is "feedbackpctcp" || _checkerConfiguration.SchedulingStrategy is "feedbackpos" || @@ -589,7 +585,6 @@ private void RunNextIteration(int schedule) try { - ShouldEmitTrace = false; // Creates a new instance of the controlled runtime. runtime = new ControlledRuntime(_checkerConfiguration, Strategy, RandomValueGenerator); @@ -651,7 +646,7 @@ private void RunNextIteration(int schedule) GatherTestingStatistics(runtime, timelineObserver); - if (ShouldEmitTrace || (!IsReplayModeEnabled && TestReport.NumOfFoundBugs > 0)) + if (!IsReplayModeEnabled && TestReport.NumOfFoundBugs > 0) { if (runtimeLogger != null) { @@ -840,7 +835,7 @@ public void TryEmitTraces(string directory, string file) Logger.WriteLine($"..... Writing {graphPath}"); } - if (!_checkerConfiguration.PerformFullExploration || ShouldEmitTrace) + if (!_checkerConfiguration.PerformFullExploration) { // Emits the reproducable trace, if it exists. if (!string.IsNullOrEmpty(ReproducableTrace)) diff --git a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs index 6b62e9f69d..5e73759ffb 100644 --- a/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs +++ b/Src/PCompiler/PCommandLine/Options/PCheckerOptions.cs @@ -233,20 +233,15 @@ private static void UpdateConfigurationWithParsedArgument(CheckerConfiguration c checkerConfiguration.RandomGeneratorSeed = (uint)option.Value; break; case "sch-random": - case "sch-rff": case "sch-pos": case "sch-feedbackpos": case "sch-feedback": - case "sch-2stagefeedback": checkerConfiguration.SchedulingStrategy = option.LongName.Substring(4); break; case "sch-probabilistic": case "sch-pct": - case "sch-pctcp": case "sch-fairpct": case "sch-feedbackpct": - case "sch-feedbackpctcp": - case "sch-2stagefeedbackpct": checkerConfiguration.SchedulingStrategy = option.LongName.Substring(4); checkerConfiguration.StrategyBound = (int)(uint)option.Value; break; From 548f0f6b8246c0374bdfc37cd4a7fb3b03df1b72 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 10 Oct 2024 16:13:41 +0800 Subject: [PATCH 10/21] Revert changes to Event. --- Src/PChecker/CheckerCore/Actors/ActorRuntime.cs | 2 -- Src/PChecker/CheckerCore/Actors/Events/Event.cs | 4 ---- Src/PChecker/CheckerCore/PRuntime/PMachine.cs | 2 -- Src/PChecker/CheckerCore/Specifications/Monitor.cs | 1 - .../Strategies/Feedback/Coverage/ISendEventMonitor.cs | 8 -------- .../Strategies/Feedback/Coverage/Operation.cs | 9 --------- 6 files changed, 26 deletions(-) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs diff --git a/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs b/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs index 690f06c901..1959d9c76e 100644 --- a/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs +++ b/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs @@ -264,8 +264,6 @@ private Actor CreateActor(ActorId id, Type type, string name, Actor creator, Gui /// internal virtual void SendEvent(ActorId targetId, Event e, Actor sender, Guid opGroupId) { - e.Sender = sender.ToString(); - e.Receiver = targetId.ToString(); var enqueueStatus = EnqueueEvent(targetId, e, sender, opGroupId, out var target); if (enqueueStatus is EnqueueStatus.EventHandlerNotRunning) { diff --git a/Src/PChecker/CheckerCore/Actors/Events/Event.cs b/Src/PChecker/CheckerCore/Actors/Events/Event.cs index 57917c052b..a33f298172 100644 --- a/Src/PChecker/CheckerCore/Actors/Events/Event.cs +++ b/Src/PChecker/CheckerCore/Actors/Events/Event.cs @@ -11,9 +11,5 @@ namespace PChecker.Actors.Events [DataContract] public abstract class Event { - public string? Sender; - public string? Receiver; - public string? State; - public int Index; } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/PRuntime/PMachine.cs b/Src/PChecker/CheckerCore/PRuntime/PMachine.cs index d95b2e38de..92014533ab 100644 --- a/Src/PChecker/CheckerCore/PRuntime/PMachine.cs +++ b/Src/PChecker/CheckerCore/PRuntime/PMachine.cs @@ -86,8 +86,6 @@ public void TrySendEvent(PMachineValue target, Event ev, object payload = null) var oneArgConstructor = ev.GetType().GetConstructors().First(x => x.GetParameters().Length > 0); ev = (Event)oneArgConstructor.Invoke(new[] { payload }); - ev.Sender = Id.ToString(); - ev.Receiver = target.Id.ToString(); AnnounceInternal(ev); SendEvent(target.Id, ev); } diff --git a/Src/PChecker/CheckerCore/Specifications/Monitor.cs b/Src/PChecker/CheckerCore/Specifications/Monitor.cs index 5314899c18..acf62c8dc9 100644 --- a/Src/PChecker/CheckerCore/Specifications/Monitor.cs +++ b/Src/PChecker/CheckerCore/Specifications/Monitor.cs @@ -275,7 +275,6 @@ protected void Assert(bool predicate, string s, params object[] args) /// internal void MonitorEvent(Event e, string senderName, string senderType, string senderState) { - e.Sender = senderName; Runtime.LogWriter.LogMonitorProcessEvent(GetType().FullName, CurrentStateName, senderName, senderType, senderState, e); HandleEvent(e); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs deleted file mode 100644 index 28014d7d24..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/ISendEventMonitor.cs +++ /dev/null @@ -1,8 +0,0 @@ -using PChecker.Actors; -using PChecker.Actors.Logging; - -public interface ISendEventMonitor { - public void OnSendEvent(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc); - - public void OnSendEventDone(ActorId sender, int loc, ActorId receiver, VectorClockGenerator currentVc); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs deleted file mode 100644 index 4af176c918..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/Operation.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace PChecker.Feedback; - -public record Operation(string Sender, string Receiver, int Loc) { - public override string ToString() - { - return $"<{Sender}, {Receiver}, {Loc}>"; - } - -} From 52dc10a01348ae105ffd103792c286c5fb19bdc2 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 11 Oct 2024 09:38:35 +0800 Subject: [PATCH 11/21] Revert changes. --- .../CheckerCore/Actors/ActorRuntime.cs | 1 + .../CheckerCore/Actors/Logging/JsonWriter.cs | 73 +++++++++---------- .../CheckerCore/Actors/Logging/LogWriter.cs | 1 + .../CheckerCore/CheckerConfiguration.cs | 4 +- .../SystematicTesting/TestingEngine.cs | 5 +- 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs b/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs index 1959d9c76e..021067e972 100644 --- a/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs +++ b/Src/PChecker/CheckerCore/Actors/ActorRuntime.cs @@ -16,6 +16,7 @@ using PChecker.Actors.Managers; using PChecker.Random; using PChecker.Runtime; +using PChecker.Runtime.Logging; using PChecker.Specifications.Monitors; using EventInfo = PChecker.Actors.Events.EventInfo; diff --git a/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs b/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs index acc6adc74f..a546bae0f2 100644 --- a/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs +++ b/Src/PChecker/CheckerCore/Actors/Logging/JsonWriter.cs @@ -6,16 +6,16 @@ using System.Security.Cryptography; using System.Text; -namespace PChecker.Actors.Logging +namespace PChecker.Runtime.Logging { /// /// Class for handling generating the vector clock for a log entry /// - public class VectorClockGenerator + internal class VectorClockGenerator { /// /// Nested class for handling FIFO send receive requests. - /// NOTE: In the case of sending to a the same machine with the same event and same payload. + /// NOTE: In the case of sending to the same machine with the same event and same payload. /// private class FifoSendReceiveMapping { @@ -61,7 +61,7 @@ internal FifoSendReceiveMapping() /// /// Field declaration that keeps track of a global vector clock map of all the machines. /// - public readonly Dictionary> ContextVcMap; + private readonly Dictionary> _contextVcMap; /// /// Field declaration that keeps track of unprocessed send requests. I.e., when a send request happened @@ -85,7 +85,7 @@ internal FifoSendReceiveMapping() /// public VectorClockGenerator() { - ContextVcMap = new Dictionary>(); + _contextVcMap = new Dictionary>(); _unhandledSendRequests = new Dictionary>(); _machines = new HashSet(); _sendRequestsCount = new Dictionary(); @@ -205,7 +205,7 @@ private void updateMachineVcMap(string machine, Dictionary senderVc { // Get a set of all machine names to update between the sender vc map and the current machine vc map (minus the current machine) var machinesToUpdateInVc = - new HashSet(ContextVcMap[machine].Keys.Union(senderVcMap.Keys).Except(new[] { machine })); + new HashSet(_contextVcMap[machine].Keys.Union(senderVcMap.Keys).Except(new[] { machine })); // Update local machine's vector clock in _contextVcMap, outside of itself, since it was already updated (incremented) from above // right before the switch case. @@ -213,17 +213,17 @@ private void updateMachineVcMap(string machine, Dictionary senderVc // the current machine's vector clock. Details can be found here: https://en.wikipedia.org/wiki/Vector_clock foreach (var machineToUpdate in machinesToUpdateInVc) { - if (ContextVcMap[machine].TryGetValue(machineToUpdate, out var localMachineToUpdateValue)) + if (_contextVcMap[machine].TryGetValue(machineToUpdate, out var localMachineToUpdateValue)) { if (senderVcMap.TryGetValue(machineToUpdate, out var senderMachineToUpdateValue)) { - ContextVcMap[machine][machineToUpdate] = + _contextVcMap[machine][machineToUpdate] = Math.Max(senderMachineToUpdateValue, localMachineToUpdateValue); } } else { - ContextVcMap[machine].Add(machineToUpdate, senderVcMap[machineToUpdate]); + _contextVcMap[machine].Add(machineToUpdate, senderVcMap[machineToUpdate]); } } } @@ -243,11 +243,11 @@ public void HandleLogEntry(LogEntry logEntry) if (MachineIsNew(machine)) { _machines.Add(machine); - ContextVcMap.Add(machine, new Dictionary { { machine, 0 } }); + _contextVcMap.Add(machine, new Dictionary { { machine, 0 } }); } // Always update the local machine count by one on any event. - ContextVcMap[machine][machine] += 1; + _contextVcMap[machine][machine] += 1; switch (logType) { @@ -273,13 +273,13 @@ public void HandleLogEntry(LogEntry logEntry) // Update the sendReqId with the send count of it. sendReqId += $":_{_sendRequestsCount[hashedGeneralSendReqId].SentCount}"; var hashedSendReqId = HashString(sendReqId); - _unhandledSendRequests.Add(hashedSendReqId, CopyVcMap(ContextVcMap[machine])); + _unhandledSendRequests.Add(hashedSendReqId, CopyVcMap(_contextVcMap[machine])); break; // For MonitorProcessEvents, tie it to the senderMachine's current vector clock // so that there is some association in the timeline case "MonitorProcessEvent": - if (logDetails.Sender != null) updateMachineVcMap(machine, ContextVcMap[logDetails.Sender]); + if (logDetails.Sender != null) updateMachineVcMap(machine, _contextVcMap[logDetails.Sender]); break; // On dequeue OR receive event, has the string containing information about the current machine that dequeued (i.e. received the event), @@ -317,7 +317,7 @@ public void HandleLogEntry(LogEntry logEntry) } // Update the log entry with the vector clock. - logEntry.Details.Clock = CopyVcMap(ContextVcMap[machine]); + logEntry.Details.Clock = CopyVcMap(_contextVcMap[machine]); } } @@ -349,23 +349,23 @@ public LogEntry() /// Enum representing the possible attributes in the details dictionary, /// which represents the data associated with a specific log type. All of /// the following are available or expanded parameters associated with an - /// IActorRuntime method. Naming for them is mostly the same except some + /// ControlledRuntime method. Naming for them is mostly the same except some /// are changed for simplicity. - /// I.e., for OnRaiseEvent(ActorId id, string, stateName, Event e), it + /// I.e., for OnRaiseEvent(StateMachineId id, string, stateName, Event e), it /// will have attributes id, state (simplified from stateName, event /// (simplified from eventName within Event e), and payload (in Event e). /// public class LogDetails { /// - /// The text log from PLogFormatter. Removes the log tags. + /// The text log from PCheckerLogTextFormatter. Removes the log tags. /// I.e., no <SomeLog> in the beginning. /// Available for all log types. /// public string? Log { get; set; } /// - /// The actor id. + /// The state machine id. /// public string? Id { get; set; } @@ -437,7 +437,7 @@ public class LogDetails public int? HaltInboxSize { get; set; } /// - /// Boolean representing whether an actor was waiting for one or more events + /// Boolean representing whether an state machine was waiting for one or more events /// Available for log type ReceiveEvent. /// public bool? WasBlocked { get; set; } @@ -449,7 +449,7 @@ public class LogDetails public string? Sender { get; set; } /// - /// Id of target actor. + /// Id of target state machine. /// Available for log type SendEvent. /// public string? Target { get; set; } @@ -461,7 +461,7 @@ public class LogDetails public string? OpGroupId { get; set; } /// - /// Boolean representing whether the target actor was halted. + /// Boolean representing whether the target state machine was halted. /// Available for log type SendEvent. /// public bool? IsTargetHalted { get; set; } @@ -533,7 +533,7 @@ public class JsonWriter /// /// Vector clock generator instance to help with vector clock generation. /// - internal VectorClockGenerator VcGenerator {get;} + private readonly VectorClockGenerator _vcGenerator; /// /// Getter for accessing log entry details. @@ -552,13 +552,13 @@ public JsonWriter() { _logs = new List(); _log = new LogEntry(); - VcGenerator = new VectorClockGenerator(); + _vcGenerator = new VectorClockGenerator(); } /// /// Enum representing the different log types the JSON error trace logs. - /// Referenced from PLogFormatter.cs and ActorRuntimeLogTextFormatter.cs - /// to see what those formatter logs. Check IActorRuntimeLog.cs to see + /// Referenced from PCheckerLogTextFormatter.cs and PCheckerLogTextFormatter.cs + /// to see what those formatter logs. Check IControlledRuntimeLog.cs to see /// each log types' description and when they are invoked. /// public enum LogType @@ -568,11 +568,6 @@ public enum LogType /// AssertionFailure, - /// - /// Invoked when the specified actor has been created. - /// - CreateActor, - /// /// Invoked when the specified state machine has been created. /// @@ -584,13 +579,13 @@ public enum LogType CreateMonitor, /// - /// Invoked when the specified actor is idle (there is nothing to dequeue) and the default + /// Invoked when the specified state machine is idle (there is nothing to dequeue) and the default /// event handler is about to be executed. /// DefaultEventHandler, /// - /// Invoked when the specified event is dequeued by an actor. + /// Invoked when the specified event is dequeued by an state machine. /// DequeueEvent, @@ -600,7 +595,7 @@ public enum LogType ExceptionHandled, /// - /// Invoked when the specified actor throws an exception. + /// Invoked when the specified state machine throws an exception. /// ExceptionThrown, @@ -610,7 +605,7 @@ public enum LogType GotoState, /// - /// Invoked when the specified actor has been halted. + /// Invoked when the specified state machine has been halted. /// Halt, @@ -652,12 +647,12 @@ public enum LogType RaiseEvent, /// - /// Invoked when the specified event is received by an actor. + /// Invoked when the specified event is received by an state machine. /// ReceiveEvent, /// - /// Invoked when the specified event is sent to a target actor. + /// Invoked when the specified event is sent to a target state machine. /// SendEvent, @@ -672,12 +667,12 @@ public enum LogType StrategyDescription, /// - /// Invoked when the specified actor waits to receive an event of a specified type. + /// Invoked when the specified state machine waits to receive an event of a specified type. /// WaitEvent, /// - /// Invoked when the specified actor waits to receive multiple events of a specified type. + /// Invoked when the specified state machine waits to receive multiple events of a specified type. /// WaitMultipleEvents, @@ -716,7 +711,7 @@ public void AddToLogs(bool updateVcMap = false) { if (updateVcMap) { - VcGenerator.HandleLogEntry(_log); + _vcGenerator.HandleLogEntry(_log); } _logs.Add(_log); diff --git a/Src/PChecker/CheckerCore/Actors/Logging/LogWriter.cs b/Src/PChecker/CheckerCore/Actors/Logging/LogWriter.cs index aad60d8aab..29b8f5aff8 100644 --- a/Src/PChecker/CheckerCore/Actors/Logging/LogWriter.cs +++ b/Src/PChecker/CheckerCore/Actors/Logging/LogWriter.cs @@ -7,6 +7,7 @@ using System.Linq; using PChecker.Actors.Events; using PChecker.IO.Logging; +using PChecker.Runtime.Logging; namespace PChecker.Actors.Logging { diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index bc296da11c..716f57a70d 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -229,7 +229,7 @@ public int MaxSchedulingSteps /// Defaults to true. /// [DataMember] - public bool IsJsonLogEnabled { get; set; } = false; + public bool IsJsonLogEnabled { get; set; } = true; /// /// If specified, requests a custom runtime log to be used instead of the default. @@ -308,7 +308,7 @@ protected CheckerConfiguration() RandomGeneratorSeed = null; IncrementalSchedulingSeed = false; PerformFullExploration = false; - MaxFairSchedulingSteps = 10000; // 10 times the unfair steps + MaxFairSchedulingSteps = 100000; // 10 times the unfair steps MaxUnfairSchedulingSteps = 10000; UserExplicitlySetMaxFairSchedulingSteps = false; TestingProcessId = 0; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index 4ada617a5a..9a4b45c375 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -26,6 +26,7 @@ using PChecker.IO.Logging; using PChecker.Random; using PChecker.Runtime; +using PChecker.Runtime.Logging; using PChecker.SystematicTesting.Strategies; using PChecker.SystematicTesting.Strategies.Exhaustive; using PChecker.SystematicTesting.Strategies.Feedback; @@ -1102,10 +1103,6 @@ private bool ShouldPrintIteration(int schedule) var count = schedule.ToString().Length - 1; var guard = "1" + (count > 0 ? string.Concat(Enumerable.Repeat("0", count)) : string.Empty); PrintGuard = int.Parse(guard); - if (PrintGuard > 1000) - { - PrintGuard = 1000; - } } return schedule % PrintGuard == 0; From 8510ec41b336f7be1b957563eeeb67e5096c94e4 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 11 Oct 2024 10:04:12 +0800 Subject: [PATCH 12/21] Fix merge conflicts. --- .../Operations/AsyncOperation.cs | 1 - .../Feedback/Coverage/TimelineObserver.cs | 147 +++++++++++++++--- 2 files changed, 127 insertions(+), 21 deletions(-) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs index 46d7112852..47d4da8cab 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using PChecker.Actors.Events; #if !DEBUG using System.Diagnostics; #endif diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs index b4503e52a2..e58aacb20b 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/TimelineObserver.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; -using PChecker.Actors; -using PChecker.Actors.Events; -using PChecker.Actors.Logging; +using PChecker.Runtime.Events; +using PChecker.Runtime.Logging; +using PChecker.Runtime.StateMachines; namespace PChecker.Feedback; -internal class TimelineObserver: ActorRuntimeLogBase +internal class TimelineObserver: IControlledRuntimeLog { private HashSet<(string, string, string)> _timelines = new(); private Dictionary> _allEvents = new(); private Dictionary> _orderedEvents = new(); + private IControlledRuntimeLog _controlledRuntimeLogImplementation; public static readonly List<(int, int)> Coefficients = new(); public static int NumOfCoefficients = 50; @@ -28,22 +29,6 @@ static TimelineObserver() } } - public override void OnDequeueEvent(ActorId id, string stateName, Event e) - { - string actor = id.Type; - - _allEvents.TryAdd(actor, new()); - _orderedEvents.TryAdd(actor, new()); - - string name = e.GetType().Name; - foreach (var ev in _allEvents[actor]) - { - _timelines.Add((actor, ev, name)); - } - _allEvents[actor].Add(name); - _orderedEvents[actor].Add(name); - } - public int GetTimelineHash() { return GetAbstractTimeline().GetHashCode(); @@ -81,4 +66,126 @@ public List GetTimelineMinhash() } return minHash; } + + public void OnCreateStateMachine(StateMachineId id, string creatorName, string creatorType) + { + } + + public void OnExecuteAction(StateMachineId id, string handlingStateName, string currentStateName, string actionName) + { + } + + public void OnSendEvent(StateMachineId targetStateMachineId, string senderName, string senderType, string senderStateName, + Event e, bool isTargetHalted) + { + } + + public void OnRaiseEvent(StateMachineId id, string stateName, Event e) + { + } + + public void OnEnqueueEvent(StateMachineId id, Event e) + { + } + + public void OnDequeueEvent(StateMachineId id, string stateName, Event e) + { + string actor = id.Type; + + _allEvents.TryAdd(actor, new()); + _orderedEvents.TryAdd(actor, new()); + + string name = e.GetType().Name; + foreach (var ev in _allEvents[actor]) + { + _timelines.Add((actor, ev, name)); + } + _allEvents[actor].Add(name); + _orderedEvents[actor].Add(name); + } + + public void OnReceiveEvent(StateMachineId id, string stateName, Event e, bool wasBlocked) + { + } + + public void OnWaitEvent(StateMachineId id, string stateName, Type eventType) + { + } + + public void OnWaitEvent(StateMachineId id, string stateName, params Type[] eventTypes) + { + } + + public void OnStateTransition(StateMachineId id, string stateName, bool isEntry) + { + } + + public void OnGotoState(StateMachineId id, string currentStateName, string newStateName) + { + } + + public void OnDefaultEventHandler(StateMachineId id, string stateName) + { + } + + public void OnHalt(StateMachineId id, int inboxSize) + { + } + + public void OnHandleRaisedEvent(StateMachineId id, string stateName, Event e) + { + } + + public void OnPopStateUnhandledEvent(StateMachineId id, string stateName, Event e) + { + } + + public void OnExceptionThrown(StateMachineId id, string stateName, string actionName, Exception ex) + { + } + + public void OnExceptionHandled(StateMachineId id, string stateName, string actionName, Exception ex) + { + } + + public void OnCreateMonitor(string monitorType) + { + } + + public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) + { + } + + public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType, + string senderStateName, Event e) + { + } + + public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) + { + } + + public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) + { + } + + public void OnMonitorError(string monitorType, string stateName, bool? isInHotState) + { + } + + public void OnRandom(object result, string callerName, string callerType) + { + } + + public void OnAssertionFailure(string error) + { + } + + public void OnStrategyDescription(string strategyName, string description) + { + } + + public void OnCompleted() + { + } } \ No newline at end of file From 626250252bc582f9c412a1f93059068ad90c5b5b Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 11 Oct 2024 10:10:35 +0800 Subject: [PATCH 13/21] Remove temp file. --- Src/PChecker/CheckerCore/CheckerConfiguration.cs | 1 - Tutorial/2_TwoPhaseCommit/timeline.txt | 0 2 files changed, 1 deletion(-) delete mode 100644 Tutorial/2_TwoPhaseCommit/timeline.txt diff --git a/Src/PChecker/CheckerCore/CheckerConfiguration.cs b/Src/PChecker/CheckerCore/CheckerConfiguration.cs index 38346ff3a9..b2c1d7d869 100644 --- a/Src/PChecker/CheckerCore/CheckerConfiguration.cs +++ b/Src/PChecker/CheckerCore/CheckerConfiguration.cs @@ -280,7 +280,6 @@ public int MaxSchedulingSteps [DataMember] public string JvmArgs; - /// /// Initializes a new instance of the class. /// diff --git a/Tutorial/2_TwoPhaseCommit/timeline.txt b/Tutorial/2_TwoPhaseCommit/timeline.txt deleted file mode 100644 index e69de29bb2..0000000000 From 907dcd1b3524ae4756b33cc5b30deb79e29fadda Mon Sep 17 00:00:00 2001 From: Ao Li Date: Sat, 12 Oct 2024 14:46:34 +0800 Subject: [PATCH 14/21] Rename LastSentReceiver to MessageReceiver --- Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs | 2 +- .../CheckerCore/SystematicTesting/Operations/AsyncOperation.cs | 2 +- .../SystematicTesting/Strategies/Probabilistic/POSScheduler.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs index ced0693165..23327d0a8d 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs @@ -618,7 +618,7 @@ private EnqueueStatus EnqueueEvent(StateMachineId targetId, Event e, StateMachin "Cannot send event '{0}' to state machine id '{1}' that is not bound to an state machine instance.", e.GetType().FullName, targetId.Value); - Scheduler.ScheduledOperation.LastSentReceiver = targetId.ToString(); + Scheduler.ScheduledOperation.MessageReceiver = targetId.ToString(); Scheduler.ScheduleNextEnabledOperation(AsyncOperationType.Send); ResetProgramCounter(sender); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs index 47d4da8cab..bbc6803e96 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Operations/AsyncOperation.cs @@ -53,7 +53,7 @@ internal abstract class AsyncOperation : IAsyncOperation /// /// The receiver if the operation is Send. /// - public string LastSentReceiver = ""; + public string MessageReceiver = ""; /// /// Initializes a new instance of the class. diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs index ae44bbec13..942d6020b6 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs @@ -49,7 +49,7 @@ void ResetPriorities(AsyncOperation next, IEnumerable ops) { if (op.Type == AsyncOperationType.Send) { - if (op.LastSentReceiver == next.LastSentReceiver) + if (op.MessageReceiver == next.MessageReceiver) { PrioritizedOperations.Remove(op); } From 8153cd682b84eaead178f62e8ad5c9184c8efd2d Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 17 Oct 2024 15:06:16 +0800 Subject: [PATCH 15/21] Simplify scheduler implementation. --- .../Feedback/FeedbackGuidedStrategy.cs | 25 +- .../Feedback/Generator/IGenerator.cs | 6 - .../Feedback/Generator/Mutator/IMutator.cs | 6 - .../Generator/Mutator/PCTScheduleMutator.cs | 17 - .../Generator/Mutator/POSScheduleMutator.cs | 14 - .../Generator/Mutator/RandomInputMutator.cs | 17 - .../Mutator/RandomScheduleMutator.cs | 17 - .../Generator/Object/ControlledRandom.cs | 69 ++++ .../Generator/Object/RandomChoices.cs | 10 +- .../Generator/PCTScheduleGenerator.cs | 62 ---- .../Generator/POSScheduleGenerator.cs | 63 ---- .../Feedback/Generator/ParametricProvider.cs | 27 -- .../Generator/RandomInputGenerator.cs | 101 ------ .../Generator/RandomScheduleGenerator.cs | 61 ---- ...{PrioritizedScheduler.cs => IScheduler.cs} | 5 +- .../Strategies/Probabilistic/PCTScheduler.cs | 323 ++++++++++-------- .../Strategies/Probabilistic/PCTStrategy.cs | 278 --------------- .../Strategies/Probabilistic/POSScheduler.cs | 22 +- .../Probabilistic/PriorizationProvider.cs | 7 - .../PriorizationSchedulingBase.cs | 194 ----------- .../RandomPriorizationProvider.cs | 27 -- .../Probabilistic/RandomScheduler.cs | 57 ++++ ...trategy.cs => ScheduleAndInputStrategy.cs} | 8 +- .../SystematicTesting/TestReport.cs | 11 +- .../SystematicTesting/TestingEngine.cs | 35 +- 25 files changed, 380 insertions(+), 1082 deletions(-) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/ControlledRandom.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/{PrioritizedScheduler.cs => IScheduler.cs} (74%) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomScheduler.cs rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/{PrioritizedSchedulingStrategy.cs => ScheduleAndInputStrategy.cs} (89%) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs index 8567bc6539..fa4ca7d04d 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/FeedbackGuidedStrategy.cs @@ -4,17 +4,17 @@ using System.Linq; using PChecker.Generator; using PChecker.Feedback; +using PChecker.Generator.Object; +using PChecker.SystematicTesting.Strategies.Probabilistic; using AsyncOperation = PChecker.SystematicTesting.Operations.AsyncOperation; using Debug = System.Diagnostics.Debug; namespace PChecker.SystematicTesting.Strategies.Feedback; -internal class FeedbackGuidedStrategy : IFeedbackGuidedStrategy - where TInput: IInputGenerator - where TSchedule: IScheduleGenerator +internal class FeedbackGuidedStrategy : IFeedbackGuidedStrategy { - public record StrategyGenerator(TInput InputGenerator, TSchedule ScheduleGenerator); + public record StrategyGenerator(ControlledRandom InputGenerator, IScheduler Scheduler); public record GeneratorRecord(int Priority, StrategyGenerator Generator, List MinHash); @@ -39,9 +39,9 @@ public record GeneratorRecord(int Priority, StrategyGenerator Generator, List /// Initializes a new instance of the class. /// - public FeedbackGuidedStrategy(CheckerConfiguration checkerConfiguration, TInput input, TSchedule schedule) + public FeedbackGuidedStrategy(CheckerConfiguration checkerConfiguration, ControlledRandom inputGenerator, IScheduler scheduler) { - if (schedule is PctScheduleGenerator) + if (scheduler is PCTScheduler) { _maxScheduledSteps = checkerConfiguration.MaxUnfairSchedulingSteps; } @@ -49,16 +49,15 @@ public FeedbackGuidedStrategy(CheckerConfiguration checkerConfiguration, TInput { _maxScheduledSteps = checkerConfiguration.MaxFairSchedulingSteps; } - Generator = new StrategyGenerator(input, schedule); + Generator = new StrategyGenerator(inputGenerator, scheduler); } /// public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) { - // var enabledOperations = _nfa != null? _nfa.FindHighPriorityOperations(ops) : ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - next = Generator.ScheduleGenerator.NextRandomOperation(ops.ToList(), current); + var result = Generator.Scheduler.GetNextOperation(current, ops, out next); ScheduledSteps++; - return next != null; + return result; } /// @@ -202,7 +201,7 @@ public int TotalSavedInputs() private void PrepareNextInput() { - Generator.ScheduleGenerator.PrepareForNextInput(); + Generator.Scheduler.PrepareForNextIteration(); if (_savedGenerators.Count == 0) { // Mutate current input if no input is saved. @@ -257,12 +256,12 @@ private void PrepareNextInput() protected virtual StrategyGenerator MutateGenerator(StrategyGenerator prev) { - return new StrategyGenerator(prev.InputGenerator.Mutate(), prev.ScheduleGenerator.Mutate()); + return new StrategyGenerator(prev.InputGenerator.Mutate(), prev.Scheduler.Mutate()); } protected virtual StrategyGenerator NewGenerator() { - return new StrategyGenerator(Generator.InputGenerator.New(), Generator.ScheduleGenerator.New()); + return new StrategyGenerator(Generator.InputGenerator.New(), Generator.Scheduler.New()); } public void DumpStats(TextWriter writer) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs index 6a043dc691..8face2fa56 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs @@ -8,11 +8,5 @@ public interface IGenerator /// A new generator. T Mutate(); - /// - /// Copy the current generator and create a new one. - /// - /// A new generator. - T Copy(); - T New(); } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs deleted file mode 100644 index 93302c6d3a..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/IMutator.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PChecker.Generator.Mutator; - -public interface IMutator -{ - T Mutate(T prev); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs deleted file mode 100644 index eec20e4076..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/PCTScheduleMutator.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace PChecker.Generator.Mutator; - -internal class PCTScheduleMutator : IMutator -{ - private int _meanMutationCount = 5; - private int _meanMutationSize = 5; - private System.Random _random = new(); - public PctScheduleGenerator Mutate(PctScheduleGenerator prev) - { - return new PctScheduleGenerator(prev.Random, - Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), - Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random), - prev.MaxPrioritySwitchPoints, - prev.ScheduleLength - ); - } -} diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs deleted file mode 100644 index b375093a0f..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/POSScheduleMutator.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace PChecker.Generator.Mutator; - -internal class POSScheduleMutator: IMutator -{ - private int _meanMutationCount = 5; - private int _meanMutationSize = 5; - private System.Random _random = new(); - public POSScheduleGenerator Mutate(POSScheduleGenerator prev) - { - return new POSScheduleGenerator(prev.Random, - Utils.MutateRandomChoices(prev.PriorityChoices, _meanMutationCount, _meanMutationSize, _random), - Utils.MutateRandomChoices(prev.SwitchPointChoices, _meanMutationCount, _meanMutationSize, _random)); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs deleted file mode 100644 index 33409f939a..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomInputMutator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.IO; -using PChecker.Generator.Object; - -namespace PChecker.Generator.Mutator; - -public class RandomInputMutator : IMutator -{ - private readonly int _meanMutationCount = 10; - private readonly int _meanMutationSize = 10; - private System.Random _random = new(); - public RandomInputGenerator Mutate(RandomInputGenerator prev) - { - return new RandomInputGenerator(prev.Random, Utils.MutateRandomChoices(prev.IntChoices, _meanMutationCount, _meanMutationSize, _random), - Utils.MutateRandomChoices(prev.DoubleChoices, _meanMutationCount, _meanMutationSize, _random)); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs deleted file mode 100644 index ac6aed409c..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/RandomScheduleMutator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using PChecker.Generator.Object; - -namespace PChecker.Generator.Mutator; - -internal class RandomScheduleMutator : IMutator -{ - private readonly int _meanMutationCount = 10; - private readonly int _meanMutationSize = 10; - private System.Random _random = new(); - public RandomScheduleGenerator Mutate(RandomScheduleGenerator prev) - { - return new RandomScheduleGenerator(prev.Random, Utils.MutateRandomChoices(prev.IntChoices, _meanMutationCount, _meanMutationSize, _random)); - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/ControlledRandom.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/ControlledRandom.cs new file mode 100644 index 0000000000..d271a830b2 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/ControlledRandom.cs @@ -0,0 +1,69 @@ +using System; +using PChecker.Generator.Mutator; +using PChecker.Random; + +namespace PChecker.Generator.Object; + +/// +/// Controlled Random. +/// +public class ControlledRandom: IRandomValueGenerator +{ + public RandomChoices IntChoices; + public RandomChoices DoubleChoices; + + /// + /// Constructor + /// + /// + /// + public ControlledRandom(RandomChoices @int, RandomChoices @double) + { + IntChoices = @int; + DoubleChoices = @double; + } + + public ControlledRandom(CheckerConfiguration checkerConfiguration) : this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode())) + { + } + + public ControlledRandom(System.Random random) + { + IntChoices = new RandomChoices(random); + DoubleChoices = new RandomChoices(random); + } + + /// + public uint Seed { get; set; } + + /// + public int Next() + { + return IntChoices.Next(); + } + + /// + public int Next(int maxValue) + { + return IntChoices.Next() % maxValue; + } + + /// + public double NextDouble() + { + return DoubleChoices.Next(); + } + + public ControlledRandom New() + { + return new ControlledRandom(IntChoices.Random); + } + + public ControlledRandom Mutate() + { + return new ControlledRandom(Utils.MutateRandomChoices(IntChoices, 5, 5, IntChoices.Random), + Utils.MutateRandomChoices(DoubleChoices, 5, 5, DoubleChoices.Random) + ); + } + +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs index e32d1e007e..ad7c65674f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs @@ -8,18 +8,18 @@ namespace PChecker.Generator.Object; public class RandomChoices where T: IConvertible { - private readonly System.Random _random; + internal readonly System.Random Random; public int Pos; public List Data = new(); public RandomChoices(System.Random random) { - _random = random; + Random = random; } public RandomChoices(RandomChoices other) { - _random = other._random; + Random = other.Random; Data = new List(other.Data); } @@ -36,11 +36,11 @@ public T GenerateNew() { if (typeof(T).IsAssignableFrom(typeof(int))) { - return (T) Convert.ChangeType(_random.Next(), typeof(T)); + return (T) Convert.ChangeType(Random.Next(), typeof(T)); } else if (typeof(T).IsAssignableFrom(typeof(double))) { - return (T) Convert.ChangeType(_random.NextDouble(), typeof(T)); + return (T) Convert.ChangeType(Random.NextDouble(), typeof(T)); } else { diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs deleted file mode 100644 index 0a0ed19401..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/PCTScheduleGenerator.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using PChecker.Generator.Mutator; -using PChecker.Generator.Object; -using PChecker.SystematicTesting.Operations; -using PChecker.SystematicTesting.Strategies.Probabilistic; - -namespace PChecker.Generator; - -internal sealed class PctScheduleGenerator: PCTScheduler, IScheduleGenerator -{ - public System.Random Random; - public RandomChoices PriorityChoices; - public RandomChoices SwitchPointChoices; - - public PctScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? switchPointChoices, int numSwitchPoints, int maxScheduleLength): - base(numSwitchPoints, maxScheduleLength, - new ParametricProvider( - priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), - switchPointChoices != null ? new RandomChoices(switchPointChoices) : new RandomChoices(random))) - { - Random = random; - var provider = (ParametricProvider) Provider; - PriorityChoices = provider.PriorityChoices; - SwitchPointChoices = provider.SwitchPointChoices; - } - - public PctScheduleGenerator(CheckerConfiguration checkerConfiguration): - this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, null, checkerConfiguration.StrategyBound, 0) - { - } - - public PctScheduleGenerator Mutate() - { - return new PCTScheduleMutator().Mutate(this); - } - - public PctScheduleGenerator New() - { - return new PctScheduleGenerator(Random, null, null, MaxPrioritySwitchPoints, ScheduleLength); - } - - public PctScheduleGenerator Copy() - { - return new PctScheduleGenerator(Random, PriorityChoices, SwitchPointChoices, MaxPrioritySwitchPoints, ScheduleLength); - } - - public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) - { - if (GetNextOperation(current, enabledOperations, out var next)) { - return next; - } else { - return null; - } - } - - - public void PrepareForNextInput() - { - PrepareForNextIteration(); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs deleted file mode 100644 index b3de2b2db1..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/POSScheduleGenerator.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using PChecker.Feedback; -using PChecker.Generator.Mutator; -using PChecker.Generator.Object; -using PChecker.SystematicTesting.Operations; -using PChecker.SystematicTesting.Strategies.Probabilistic; - -namespace PChecker.Generator; - -internal class POSScheduleGenerator: POSScheduler, IScheduleGenerator -{ - public System.Random Random; - public RandomChoices PriorityChoices; - public RandomChoices SwitchPointChoices; - - public POSScheduleGenerator(System.Random random, RandomChoices? priorityChoices, RandomChoices? switchPointChoices): - base(new ParametricProvider( - priorityChoices != null ? new RandomChoices(priorityChoices) : new RandomChoices(random), - switchPointChoices != null ? new RandomChoices(switchPointChoices) : new RandomChoices(random))) - { - Random = random; - var provider = (ParametricProvider) Provider; - PriorityChoices = provider.PriorityChoices; - SwitchPointChoices = provider.SwitchPointChoices; - } - - public POSScheduleGenerator(CheckerConfiguration checkerConfiguration): - this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, - null) - { - } - - public POSScheduleGenerator Mutate() - { - return new POSScheduleMutator().Mutate(this); - } - - public POSScheduleGenerator New() - { - return new POSScheduleGenerator(Random, null, null); - } - - public POSScheduleGenerator Copy() - { - return new POSScheduleGenerator(Random, PriorityChoices, SwitchPointChoices); - } - - public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current) - { - if (GetNextOperation(current, enabledOperations, out var next)) { - return next; - } else { - return null; - } - } - - public void PrepareForNextInput() - { - PrepareForNextIteration(); - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs deleted file mode 100644 index 0ffc657564..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ParametricProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using PChecker.Generator.Object; -using PChecker.SystematicTesting.Strategies.Probabilistic; - -namespace PChecker.Generator; - -internal class ParametricProvider: PriorizationProvider -{ - public RandomChoices PriorityChoices; - public RandomChoices SwitchPointChoices; - public ParametricProvider(RandomChoices priority, RandomChoices switchPoint) - { - PriorityChoices = priority; - SwitchPointChoices = switchPoint; - } - - public int AssignPriority(int numOps) - { - - return PriorityChoices.Next() % numOps + 1; - } - - public double SwitchPointChoice() - { - return SwitchPointChoices.Next(); - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs deleted file mode 100644 index bde03ddd3c..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomInputGenerator.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using PChecker.Generator.Mutator; -using PChecker.Generator.Object; - -namespace PChecker.Generator -{ - - /// - /// This class implements a JQF-style stream-based input generator. - /// See more: https://github.com/rohanpadhye/JQF - /// - public class RandomInputGenerator : IInputGenerator - { - /// - /// Device for generating random numbers. - /// - internal readonly System.Random Random; - - internal RandomChoices IntChoices; - internal RandomChoices DoubleChoices; - - public RandomInputGenerator(System.Random random, RandomChoices? intChoices, RandomChoices? doubleChoices) - { - Random = random; - IntChoices = intChoices != null ? new RandomChoices(intChoices) : new RandomChoices(Random); - DoubleChoices = doubleChoices != null ? new RandomChoices(doubleChoices) : new RandomChoices(Random); - } - - - /// - /// Create a stream based value generator using CheckerConfiguration. - /// - /// - public RandomInputGenerator(CheckerConfiguration checkerConfiguration): - this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()), null, null) - { - } - - /// - /// Create a default stream based value generator. - /// - public RandomInputGenerator(): - this(new System.Random(Guid.NewGuid().GetHashCode()), null, null) - { - } - - - /// - /// Create a stream based value generator with an existing generator. - /// - /// - public RandomInputGenerator(RandomInputGenerator other) : this(other.Random, other.IntChoices, other.DoubleChoices) - { - } - - /// - /// Returns a non-negative random number. - /// - public int Next() - { - return IntChoices.Next(); - } - - - /// - /// Returns a non-negative random number less than the specified max value. - /// - /// Exclusive upper bound. - public int Next(int maxValue) - { - var value = maxValue == 0 ? 0 : Next() % maxValue; - return value; - } - - /// - /// Returns a random floating-point number that is greater - /// than or equal to 0.0, and less than 1.0. - /// - public double NextDouble() - { - return DoubleChoices.Next(); - } - - public RandomInputGenerator Mutate() - { - return new RandomInputMutator().Mutate(this); - } - - public RandomInputGenerator Copy() - { - return new RandomInputGenerator(this); - } - - public RandomInputGenerator New() - { - return new RandomInputGenerator(Random, null, null); - } - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs deleted file mode 100644 index 49d378c87c..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomScheduleGenerator.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PChecker.Generator.Mutator; -using PChecker.Generator.Object; -using PChecker.SystematicTesting.Operations; - -namespace PChecker.Generator; - -internal class RandomScheduleGenerator: IScheduleGenerator -{ - internal readonly System.Random Random; - - public RandomChoices IntChoices; - - public RandomScheduleGenerator(System.Random random, RandomChoices? intChoices) - { - Random = random; - IntChoices = intChoices != null ? new RandomChoices(intChoices) : new RandomChoices(Random); - } - - public RandomScheduleGenerator(CheckerConfiguration checkerConfiguration): - this(new System.Random((int?)checkerConfiguration.RandomGeneratorSeed ?? Guid.NewGuid().GetHashCode()) , null) - { - } - - public AsyncOperation? NextRandomOperation(List ops, AsyncOperation current) - { - var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - if (enabledOperations.Count == 0) - { - return null; - } - - if (enabledOperations.Count == 1) - { - return enabledOperations[0]; - } - var idx = IntChoices.Next() % enabledOperations.Count; - return enabledOperations[idx]; - } - - public RandomScheduleGenerator Mutate() - { - return new RandomScheduleMutator().Mutate(this); - } - - public RandomScheduleGenerator Copy() - { - return new RandomScheduleGenerator(Random, IntChoices); - } - - public void PrepareForNextInput() - { - } - - public RandomScheduleGenerator New() - { - return new RandomScheduleGenerator(Random, null); - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/IScheduler.cs similarity index 74% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/IScheduler.cs index 7c38a0629c..1d43076f40 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/IScheduler.cs @@ -1,11 +1,14 @@ using System.Collections.Generic; +using PChecker.Random; using PChecker.SystematicTesting.Operations; namespace PChecker.SystematicTesting.Strategies.Probabilistic; -internal interface PrioritizedScheduler +internal interface IScheduler { public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next); public void Reset(); public bool PrepareForNextIteration(); + public IScheduler Mutate(); + public IScheduler New(); } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs index c0263c68e1..56b15b353f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs @@ -1,189 +1,236 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; using System.Collections.Generic; using System.Linq; +using PChecker.Generator.Object; +using PChecker.IO.Debugging; +using PChecker.Random; using PChecker.SystematicTesting.Operations; -using Debug = PChecker.IO.Debugging.Debug; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic; -internal class PCTScheduler: PrioritizedScheduler +namespace PChecker.SystematicTesting.Strategies.Probabilistic { - public readonly PriorizationProvider Provider; - - /// - /// The number of scheduled steps. - /// - private int ScheduledSteps; - - /// - /// Max number of priority switch points. - /// - public readonly int MaxPrioritySwitchPoints; - - /// - /// Approximate length of the schedule across all iterations. - /// - public int ScheduleLength; - - /// - /// List of prioritized operations. - /// - private readonly List PrioritizedOperations; - - private int _nextPriorityChangePoint; - private int _numSwitchPointsLeft; - /// - /// Initializes a new instance of the class. + /// A priority-based probabilistic scheduling strategy. /// - public PCTScheduler(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider) - { - Provider = provider; - ScheduledSteps = 0; - ScheduleLength = scheduleLength; - MaxPrioritySwitchPoints = maxPrioritySwitchPoints; - PrioritizedOperations = new List(); - _numSwitchPointsLeft = maxPrioritySwitchPoints; - - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); - } - - public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + /// + /// This strategy is described in the following paper: + /// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/asplos277-pct.pdf + /// + internal class PCTScheduler : IScheduler { - ScheduledSteps++; - next = null; - var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - if (enabledOperations.Count == 0) + + + /// + /// Random value generator. + /// + private readonly IRandomValueGenerator _randomValueGenerator; + + /// + /// The maximum number of steps to schedule. + /// + private readonly int MaxScheduledSteps; + + /// + /// The number of scheduled steps. + /// + private int ScheduledSteps; + + /// + /// Max number of priority switch points. + /// + internal readonly int MaxPrioritySwitchPoints; + + /// + /// Approximate length of the schedule across all schedules. + /// + internal int ScheduleLength; + + /// + /// List of prioritized operations. + /// + private readonly List PrioritizedOperations; + + /// + /// Next execution point where the priority should be changed. + /// + private int _nextPriorityChangePoint; + + /// + /// Number of switch points left (leq MaxPrioritySwitchPoints). + /// + private int _numSwitchPointsLeft; + + /// + /// Initializes a new instance of the class. + /// + public PCTScheduler(int maxPrioritySwitchPoints, int scheduleLength, IRandomValueGenerator random) { - if (_nextPriorityChangePoint == ScheduledSteps) + _randomValueGenerator = random; + ScheduledSteps = 0; + ScheduleLength = scheduleLength; + MaxPrioritySwitchPoints = maxPrioritySwitchPoints; + PrioritizedOperations = new List(); + double switchPointProbability = 0.1; + if (ScheduleLength != 0) { - MovePriorityChangePointForward(); + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); } - return false; - } + _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, random.NextDouble()); - var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); - if (next is null) - { - next = highestEnabledOp; } - return true; - } - - private void MovePriorityChangePointForward() - { - _nextPriorityChangePoint += 1; - Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); - } - private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) - { - AsyncOperation prioritizedOp = null; - foreach (var entity in PrioritizedOperations) + public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, + out AsyncOperation next) { - if (choices.Any(m => m == entity)) + ScheduledSteps++; + next = null; + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) { - prioritizedOp = entity; - break; + if (_nextPriorityChangePoint == ScheduledSteps) + { + MovePriorityChangePointForward(); + } + + return false; } - } - return prioritizedOp; - } + var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); + if (next is null) + { + next = highestEnabledOp; + } + return true; + } - /// - /// Returns the prioritized operation. - /// - private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) - { - if (PrioritizedOperations.Count == 0) + private void MovePriorityChangePointForward() { - PrioritizedOperations.Add(current); + _nextPriorityChangePoint += 1; + Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); } - foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) + private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) { - var mIndex = Provider.AssignPriority(PrioritizedOperations.Count); - PrioritizedOperations.Insert(mIndex, op); - Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); + AsyncOperation prioritizedOp = null; + foreach (var entity in PrioritizedOperations) + { + if (choices.Any(m => m == entity)) + { + prioritizedOp = entity; + break; + } + } + + return prioritizedOp; } - var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); - if (_nextPriorityChangePoint == ScheduledSteps) + /// + /// Returns the prioritized operation. + /// + private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) { - if (ops.Count == 1) + if (PrioritizedOperations.Count == 0) { - MovePriorityChangePointForward(); + PrioritizedOperations.Add(current); } - else + + foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) { - PrioritizedOperations.Remove(prioritizedSchedulable); - PrioritizedOperations.Add(prioritizedSchedulable); - Debug.WriteLine(" Operation '{0}' changes to lowest priority.", prioritizedSchedulable); + var mIndex = _randomValueGenerator.Next(PrioritizedOperations.Count) + 1; + PrioritizedOperations.Insert(mIndex, op); + Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); + } + - _numSwitchPointsLeft -= 1; - // Update the next priority change point. - if (_numSwitchPointsLeft > 0) + var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); + if (_nextPriorityChangePoint == ScheduledSteps) + { + if (ops.Count == 1) { - double switchPointProbability = 0.1; - if (ScheduleLength != 0) + MovePriorityChangePointForward(); + } + else + { + PrioritizedOperations.Remove(prioritizedSchedulable); + PrioritizedOperations.Add(prioritizedSchedulable); + Debug.WriteLine(" Operation '{0}' changes to lowest priority.", prioritizedSchedulable); + + _numSwitchPointsLeft -= 1; + // Update the next priority change point. + if (_numSwitchPointsLeft > 0) { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + + _nextPriorityChangePoint = + Generator.Mutator.Utils.SampleGeometric(switchPointProbability, + _randomValueGenerator.NextDouble()) + ScheduledSteps; } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()) + ScheduledSteps; - } + } } - } - if (Debug.IsEnabled) - { - Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); - Debug.Write(" Priority list: "); - for (var idx = 0; idx < PrioritizedOperations.Count; idx++) + if (Debug.IsEnabled) { - if (idx < PrioritizedOperations.Count - 1) - { - Debug.Write("'{0}', ", PrioritizedOperations[idx]); - } - else + Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); + Debug.Write(" Priority list: "); + for (var idx = 0; idx < PrioritizedOperations.Count; idx++) { - Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); + if (idx < PrioritizedOperations.Count - 1) + { + Debug.Write("'{0}', ", PrioritizedOperations[idx]); + } + else + { + Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); + } } } + + return ops.First(op => op.Equals(prioritizedSchedulable)); } - return ops.First(op => op.Equals(prioritizedSchedulable)); - } + public void Reset() + { + ScheduleLength = 0; + ScheduledSteps = 0; + PrioritizedOperations.Clear(); + } - public void Reset() - { - ScheduleLength = 0; - ScheduledSteps = 0; - PrioritizedOperations.Clear(); - } + /// + public virtual bool PrepareForNextIteration() + { + ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); + ScheduledSteps = 0; + _numSwitchPointsLeft = MaxPrioritySwitchPoints; - /// - public virtual bool PrepareForNextIteration() - { - ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); - ScheduledSteps = 0; - _numSwitchPointsLeft = MaxPrioritySwitchPoints; + PrioritizedOperations.Clear(); + double switchPointProbability = 0.1; + if (ScheduleLength != 0) + { + switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + } + + _nextPriorityChangePoint = + Generator.Mutator.Utils.SampleGeometric(switchPointProbability, _randomValueGenerator.NextDouble()); + return true; + } + + public IScheduler Mutate() + { + return new PCTScheduler(MaxPrioritySwitchPoints, ScheduleLength, ((ControlledRandom) _randomValueGenerator).Mutate()); + } - PrioritizedOperations.Clear(); - double switchPointProbability = 0.1; - if (ScheduleLength != 0) + public IScheduler New() { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); + return new PCTScheduler(MaxPrioritySwitchPoints, ScheduleLength, ((ControlledRandom) _randomValueGenerator).New()); } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); - return true; } -} +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs deleted file mode 100644 index 3ffdc47c3c..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Linq; -using PChecker.IO.Debugging; -using PChecker.Random; -using PChecker.SystematicTesting.Operations; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic -{ - /// - /// A priority-based probabilistic scheduling strategy. - /// - /// - /// This strategy is described in the following paper: - /// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/asplos277-pct.pdf - /// - internal sealed class PCTStrategy : ISchedulingStrategy - { - /// - /// Random value generator. - /// - private readonly IRandomValueGenerator RandomValueGenerator; - - /// - /// The maximum number of steps to schedule. - /// - private readonly int MaxScheduledSteps; - - /// - /// The number of scheduled steps. - /// - private int ScheduledSteps; - - /// - /// Max number of priority switch points. - /// - private readonly int MaxPrioritySwitchPoints; - - /// - /// Approximate length of the schedule across all schedules. - /// - private int ScheduleLength; - - /// - /// List of prioritized operations. - /// - private readonly List PrioritizedOperations; - - /// - /// Set of priority change points. - /// - private readonly SortedSet PriorityChangePoints; - - /// - /// Initializes a new instance of the class. - /// - public PCTStrategy(int maxSteps, int maxPrioritySwitchPoints, IRandomValueGenerator random) - { - RandomValueGenerator = random; - MaxScheduledSteps = maxSteps; - ScheduledSteps = 0; - ScheduleLength = 0; - MaxPrioritySwitchPoints = maxPrioritySwitchPoints; - PrioritizedOperations = new List(); - PriorityChangePoints = new SortedSet(); - } - - /// - public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) - { - next = null; - var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - if (enabledOperations.Count == 0) - { - return false; - } - - var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); - if (next is null) - { - next = highestEnabledOp; - } - - ScheduledSteps++; - - return true; - } - - /// - public bool GetNextBooleanChoice(AsyncOperation current, int maxValue, out bool next) - { - next = false; - if (RandomValueGenerator.Next(maxValue) == 0) - { - next = true; - } - - ScheduledSteps++; - - return true; - } - - /// - public bool GetNextIntegerChoice(AsyncOperation current, int maxValue, out int next) - { - next = RandomValueGenerator.Next(maxValue); - ScheduledSteps++; - return true; - } - - /// - public bool PrepareForNextIteration() - { - ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); - ScheduledSteps = 0; - - PrioritizedOperations.Clear(); - PriorityChangePoints.Clear(); - - var range = new List(); - for (var idx = 0; idx < ScheduleLength; idx++) - { - range.Add(idx); - } - - foreach (var point in Shuffle(range).Take(MaxPrioritySwitchPoints)) - { - PriorityChangePoints.Add(point); - } - - return true; - } - - /// - public int GetScheduledSteps() => ScheduledSteps; - - /// - public bool HasReachedMaxSchedulingSteps() - { - if (MaxScheduledSteps == 0) - { - return false; - } - - return ScheduledSteps >= MaxScheduledSteps; - } - - /// - public bool IsFair() => false; - - /// - public string GetDescription() - { - var text = $"pct[priority change points '{MaxPrioritySwitchPoints}' [" + - string.Join(", ", PriorityChangePoints.ToArray()) + - "], seed '" + RandomValueGenerator.Seed + "']"; - return text; - } - - /// - /// Returns the prioritized operation. - /// - private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) - { - if (PrioritizedOperations.Count == 0) - { - PrioritizedOperations.Add(current); - } - - foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) - { - var mIndex = RandomValueGenerator.Next(PrioritizedOperations.Count) + 1; - PrioritizedOperations.Insert(mIndex, op); - Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); - } - - if (PriorityChangePoints.Contains(ScheduledSteps)) - { - if (ops.Count == 1) - { - MovePriorityChangePointForward(); - } - else - { - var priority = GetHighestPriorityEnabledOperation(ops); - PrioritizedOperations.Remove(priority); - PrioritizedOperations.Add(priority); - Debug.WriteLine(" Operation '{0}' changes to lowest priority.", priority); - } - } - - var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); - if (Debug.IsEnabled) - { - Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); - Debug.Write(" Priority list: "); - for (var idx = 0; idx < PrioritizedOperations.Count; idx++) - { - if (idx < PrioritizedOperations.Count - 1) - { - Debug.Write("'{0}', ", PrioritizedOperations[idx]); - } - else - { - Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); - } - } - } - - return ops.First(op => op.Equals(prioritizedSchedulable)); - } - - /// - /// Returns the highest-priority enabled operation. - /// - private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) - { - AsyncOperation prioritizedOp = null; - foreach (var entity in PrioritizedOperations) - { - if (choices.Any(m => m == entity)) - { - prioritizedOp = entity; - break; - } - } - - return prioritizedOp; - } - - /// - /// Shuffles the specified list using the Fisher-Yates algorithm. - /// - private IList Shuffle(IList list) - { - var result = new List(list); - for (var idx = result.Count - 1; idx >= 1; idx--) - { - var point = RandomValueGenerator.Next(ScheduleLength); - var temp = result[idx]; - result[idx] = result[point]; - result[point] = temp; - } - - return result; - } - - /// - /// Moves the current priority change point forward. This is a useful - /// optimization when a priority change point is assigned in either a - /// sequential execution or a nondeterministic choice. - /// - private void MovePriorityChangePointForward() - { - PriorityChangePoints.Remove(ScheduledSteps); - var newPriorityChangePoint = ScheduledSteps + 1; - while (PriorityChangePoints.Contains(newPriorityChangePoint)) - { - newPriorityChangePoint++; - } - - PriorityChangePoints.Add(newPriorityChangePoint); - Debug.WriteLine(" Moving priority change to '{0}'.", newPriorityChangePoint); - } - - /// - public void Reset() - { - ScheduleLength = 0; - ScheduledSteps = 0; - PrioritizedOperations.Clear(); - PriorityChangePoints.Clear(); - } - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs index 942d6020b6..548c1fb87f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs @@ -2,14 +2,16 @@ using System.Collections.Generic; using System.Linq; using PChecker.Feedback; +using PChecker.Generator.Object; using PChecker.IO.Debugging; +using PChecker.Random; using PChecker.SystematicTesting.Operations; namespace PChecker.SystematicTesting.Strategies.Probabilistic; -internal class POSScheduler: PrioritizedScheduler +internal class POSScheduler: IScheduler { - internal readonly PriorizationProvider Provider; + private IRandomValueGenerator _randomValueGenerator; /// /// List of prioritized operations. @@ -19,9 +21,9 @@ internal class POSScheduler: PrioritizedScheduler /// /// Initializes a new instance of the class. /// - public POSScheduler(PriorizationProvider provider) + public POSScheduler(IRandomValueGenerator random) { - Provider = provider; + _randomValueGenerator = random; PrioritizedOperations = new List(); } @@ -85,7 +87,7 @@ private AsyncOperation GetPrioritizedOperation(List ops, AsyncOp foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) { - var mIndex = Provider.AssignPriority(PrioritizedOperations.Count); + var mIndex = _randomValueGenerator.Next(PrioritizedOperations.Count) + 1; PrioritizedOperations.Insert(mIndex, op); Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); } @@ -145,4 +147,14 @@ public virtual bool PrepareForNextIteration() PrioritizedOperations.Clear(); return true; } + + public IScheduler Mutate() + { + return new POSScheduler(((ControlledRandom)_randomValueGenerator).Mutate()); + } + + public IScheduler New() + { + return new POSScheduler(((ControlledRandom)_randomValueGenerator).New()); + } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs deleted file mode 100644 index d4cb446516..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace PChecker.SystematicTesting.Strategies.Probabilistic; - -public interface PriorizationProvider -{ - public int AssignPriority(int numOps); - public double SwitchPointChoice(); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs deleted file mode 100644 index ae4eee7531..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PriorizationSchedulingBase.cs +++ /dev/null @@ -1,194 +0,0 @@ - - -using System.Collections.Generic; -using System.Linq; -using PChecker.Feedback; -using PChecker.SystematicTesting.Operations; -using PChecker.Generator.Mutator; -using PChecker.IO.Debugging; -using System; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic; - -internal class PriorizationSchedulingBase -{ - public readonly PriorizationProvider Provider; - - /// - /// The number of scheduled steps. - /// - private int ScheduledSteps; - - /// - /// Max number of priority switch points. - /// - public readonly int MaxPrioritySwitchPoints; - - /// - /// Approximate length of the schedule across all iterations. - /// - public int ScheduleLength; - - /// - /// List of prioritized operations. - /// - private readonly List PrioritizedOperations; - - private int _nextPriorityChangePoint; - private int _numSwitchPointsLeft; - - /// - /// Initializes a new instance of the class. - /// - public PriorizationSchedulingBase(int maxPrioritySwitchPoints, int scheduleLength, PriorizationProvider provider) - { - Provider = provider; - ScheduledSteps = 0; - ScheduleLength = scheduleLength; - MaxPrioritySwitchPoints = maxPrioritySwitchPoints; - PrioritizedOperations = new List(); - _numSwitchPointsLeft = maxPrioritySwitchPoints; - - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); - } - - public virtual bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) - { - ScheduledSteps++; - next = null; - var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); - if (enabledOperations.Count == 0) - { - if (_nextPriorityChangePoint == ScheduledSteps) - { - MovePriorityChangePointForward(); - } - return false; - } - - var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); - if (next is null) - { - next = highestEnabledOp; - } - return true; - } - - - private void MovePriorityChangePointForward() - { - _nextPriorityChangePoint += 1; - Debug.WriteLine(" Moving priority change to '{0}'.", _nextPriorityChangePoint); - } - private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) - { - AsyncOperation prioritizedOp = null; - foreach (var entity in PrioritizedOperations) - { - if (choices.Any(m => m == entity)) - { - prioritizedOp = entity; - break; - } - } - - return prioritizedOp; - } - - - - /// - /// Returns the prioritized operation. - /// - private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) - { - if (PrioritizedOperations.Count == 0) - { - PrioritizedOperations.Add(current); - } - - foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) - { - var mIndex = Provider.AssignPriority(PrioritizedOperations.Count); - PrioritizedOperations.Insert(mIndex, op); - Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); - } - - var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); - if (_nextPriorityChangePoint == ScheduledSteps) - { - if (ops.Count == 1) - { - MovePriorityChangePointForward(); - } - else - { - PrioritizedOperations.Remove(prioritizedSchedulable); - PrioritizedOperations.Add(prioritizedSchedulable); - Debug.WriteLine(" Operation '{0}' changes to lowest priority.", prioritizedSchedulable); - - _numSwitchPointsLeft -= 1; - // Update the next priority change point. - if (_numSwitchPointsLeft > 0) - { - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()) + ScheduledSteps; - } - - } - } - - if (Debug.IsEnabled) - { - Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); - Debug.Write(" Priority list: "); - for (var idx = 0; idx < PrioritizedOperations.Count; idx++) - { - if (idx < PrioritizedOperations.Count - 1) - { - Debug.Write("'{0}', ", PrioritizedOperations[idx]); - } - else - { - Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); - } - } - } - - return ops.First(op => op.Equals(prioritizedSchedulable)); - } - - - public void Reset() - { - ScheduleLength = 0; - ScheduledSteps = 0; - PrioritizedOperations.Clear(); - } - - /// - public virtual bool PrepareForNextIteration() - { - ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); - ScheduledSteps = 0; - _numSwitchPointsLeft = MaxPrioritySwitchPoints; - - PrioritizedOperations.Clear(); - double switchPointProbability = 0.1; - if (ScheduleLength != 0) - { - switchPointProbability = 1.0 * _numSwitchPointsLeft / (ScheduleLength - ScheduledSteps + 1); - } - _nextPriorityChangePoint = Generator.Mutator.Utils.SampleGeometric(switchPointProbability, Provider.SwitchPointChoice()); - return true; - } -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs deleted file mode 100644 index c757366cd4..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomPriorizationProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using PChecker.Random; - -namespace PChecker.SystematicTesting.Strategies.Probabilistic; - -internal class RandomPriorizationProvider: PriorizationProvider -{ - - /// - /// Random value generator. - /// - private readonly IRandomValueGenerator RandomValueGenerator; - - public RandomPriorizationProvider(IRandomValueGenerator generator) - { - RandomValueGenerator = generator; - } - public int AssignPriority(int numOps) - { - return RandomValueGenerator.Next(numOps) + 1; - } - - public double SwitchPointChoice() - { - return RandomValueGenerator.NextDouble(); - } - -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomScheduler.cs new file mode 100644 index 0000000000..4194e51d94 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomScheduler.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.Generator.Mutator; +using PChecker.Generator.Object; +using PChecker.Random; +using PChecker.SystematicTesting.Operations; +using PChecker.SystematicTesting.Strategies.Probabilistic; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic; + +internal class RandomScheduler : IScheduler +{ + private readonly IRandomValueGenerator _randomValueGenerator; + public RandomScheduler(IRandomValueGenerator randomValueGenerator) + { + _randomValueGenerator = randomValueGenerator; + } + + public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + next = null; + if (enabledOperations.Count == 0) + { + return false; + } + + if (enabledOperations.Count == 1) + { + next = enabledOperations[0]; + return true; + } + var idx = _randomValueGenerator.Next(enabledOperations.Count); + next = enabledOperations[idx]; + return true; + } + + public void Reset() + { + } + + public bool PrepareForNextIteration() + { + return true; + } + + IScheduler IScheduler.Mutate() + { + return new RandomScheduler(((ControlledRandom)_randomValueGenerator).Mutate()); + } + + IScheduler IScheduler.New() + { + return new RandomScheduler(((ControlledRandom)_randomValueGenerator).New()); + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/ScheduleAndInputStrategy.cs similarity index 89% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/ScheduleAndInputStrategy.cs index 6f50c24677..1491786e1f 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PrioritizedSchedulingStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/ScheduleAndInputStrategy.cs @@ -18,7 +18,7 @@ namespace PChecker.SystematicTesting.Strategies.Probabilistic /// This strategy is described in the following paper: /// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/asplos277-pct.pdf /// - internal class PrioritizedSchedulingStrategy: ISchedulingStrategy + internal class ScheduleAndInputStrategy: ISchedulingStrategy { /// @@ -33,12 +33,12 @@ internal class PrioritizedSchedulingStrategy: ISchedulingStrategy private int _scheduledSteps; - internal readonly PrioritizedScheduler Scheduler; + internal readonly IScheduler Scheduler; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public PrioritizedSchedulingStrategy(int maxSteps, IRandomValueGenerator random, PrioritizedScheduler scheduler) + public ScheduleAndInputStrategy(int maxSteps, IRandomValueGenerator random, IScheduler scheduler) { RandomValueGenerator = random; MaxScheduledSteps = maxSteps; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs index 6e097b5be8..e8be37de16 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestReport.cs @@ -105,7 +105,8 @@ public class TestReport /// Set of hashes of timelines discovered by the scheduler. /// [DataMember] - public Dictionary ExploredTimelines = new(); + public HashSet ExploredTimelines = new(); + /// /// Lock for the test report. @@ -153,6 +154,7 @@ public bool Merge(TestReport testReport) lock (Lock) { CoverageInfo.Merge(testReport.CoverageInfo); + ExploredTimelines.UnionWith(testReport.ExploredTimelines); NumOfFoundBugs += testReport.NumOfFoundBugs; @@ -238,6 +240,13 @@ public string GetText(CheckerConfiguration checkerConfiguration, string prefix = prefix.Equals("...") ? "....." : prefix, totalExploredSchedules, totalExploredSchedules == 1 ? string.Empty : "s"); + + report.AppendLine(); + report.AppendFormat( + "{0} Explored {1} timeline{2}", + prefix.Equals("...") ? "....." : prefix, + ExploredTimelines.Count, + ExploredTimelines.Count == 1 ? string.Empty : "s"); if (totalExploredSchedules > 0 && NumOfFoundBugs > 0) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index d461c72e51..d0bfe9974d 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -19,6 +19,7 @@ using PChecker.Feedback; using PChecker.Generator; using PChecker.Generator.Mutator; +using PChecker.Generator.Object; using PChecker.IO; using PChecker.IO.Debugging; using PChecker.IO.Logging; @@ -285,27 +286,29 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "random") { - Strategy = new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); + var scheduler = new RandomScheduler(RandomValueGenerator); + Strategy = new ScheduleAndInputStrategy(checkerConfiguration.MaxFairSchedulingSteps, + RandomValueGenerator, scheduler); } else if (checkerConfiguration.SchedulingStrategy is "pct") { var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, - new RandomPriorizationProvider(RandomValueGenerator)); - Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, + RandomValueGenerator); + Strategy = new ScheduleAndInputStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, scheduler); } else if (checkerConfiguration.SchedulingStrategy is "pos") { - var scheduler = new POSScheduler(new RandomPriorizationProvider(RandomValueGenerator)); - Strategy = new PrioritizedSchedulingStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, + var scheduler = new POSScheduler(RandomValueGenerator); + Strategy = new ScheduleAndInputStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, RandomValueGenerator, scheduler); } else if (checkerConfiguration.SchedulingStrategy is "fairpct") { var prefixLength = checkerConfiguration.MaxUnfairSchedulingSteps; var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, - new RandomPriorizationProvider(RandomValueGenerator)); - var prefixStrategy = new PrioritizedSchedulingStrategy(prefixLength, RandomValueGenerator, scheduler); + RandomValueGenerator); + var prefixStrategy = new ScheduleAndInputStrategy(prefixLength, RandomValueGenerator, scheduler); var suffixStrategy = new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); Strategy = new ComboStrategy(prefixStrategy, suffixStrategy); @@ -325,21 +328,18 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "feedback") { - Strategy = new FeedbackGuidedStrategy( - _checkerConfiguration, new RandomInputGenerator(checkerConfiguration), - new RandomScheduleGenerator(checkerConfiguration)); + Strategy = new FeedbackGuidedStrategy(checkerConfiguration, new ControlledRandom(checkerConfiguration), + new RandomScheduler(new ControlledRandom(checkerConfiguration))); } else if (checkerConfiguration.SchedulingStrategy is "feedbackpct") { - Strategy = new FeedbackGuidedStrategy(_checkerConfiguration, - new RandomInputGenerator(checkerConfiguration), new PctScheduleGenerator(checkerConfiguration)); + Strategy = new FeedbackGuidedStrategy(checkerConfiguration, new ControlledRandom(checkerConfiguration), + new PCTScheduler(checkerConfiguration.StrategyBound, 0, new ControlledRandom(checkerConfiguration))); } else if (checkerConfiguration.SchedulingStrategy is "feedbackpos") { - Strategy = new FeedbackGuidedStrategy( - _checkerConfiguration, - new RandomInputGenerator(checkerConfiguration), - new POSScheduleGenerator(_checkerConfiguration)); + Strategy = new FeedbackGuidedStrategy(checkerConfiguration, new ControlledRandom(checkerConfiguration), + new POSScheduler(new ControlledRandom(checkerConfiguration))); } else if (checkerConfiguration.SchedulingStrategy is "portfolio") { @@ -967,8 +967,7 @@ private void GatherTestingStatistics(ControlledRuntime runtime, TimelineObserver report.CoverageInfo.Merge(coverageInfo); TestReport.Merge(report); var timelineHash = timelineObserver.GetTimelineHash(); - TestReport.ExploredTimelines[timelineHash] = - TestReport.ExploredTimelines.GetValueOrDefault(timelineHash, 0) + 1; + TestReport.ExploredTimelines.Add(timelineObserver.GetTimelineHash()); // Also save the graph snapshot of the last iteration, if there is one. Graph = coverageInfo.CoverageGraph; // Also save the graph snapshot of the last schedule, if there is one. From d8331b84d0729ad3ec1b32ad48909060dd40b682 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 17 Oct 2024 15:09:31 +0800 Subject: [PATCH 16/21] Revert changes in QLearning strategy. --- .../Probabilistic/QLearningStrategy.cs | 67 ++++--------------- 1 file changed, 12 insertions(+), 55 deletions(-) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs index 79b29448c4..ba90bcf613 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/QLearningStrategy.cs @@ -3,20 +3,17 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using PChecker.Feedback; using System.Text; using PChecker.Random; using PChecker.SystematicTesting.Operations; -using PChecker.SystematicTesting.Strategies.Feedback; namespace PChecker.SystematicTesting.Strategies.Probabilistic { /// /// A probabilistic scheduling strategy that uses Q-learning. /// - internal class QLearningStrategy : RandomStrategy, IFeedbackGuidedStrategy + internal class QLearningStrategy : RandomStrategy { /// /// Map from program states to a map from next operations to their quality values. @@ -81,7 +78,6 @@ internal class QLearningStrategy : RandomStrategy, IFeedbackGuidedStrategy /// private int Epochs; - /// /// Initializes a new instance of the class. /// It uses the specified random number generator. @@ -152,6 +148,7 @@ public override bool GetNextIntegerChoice(AsyncOperation current, int maxValue, /// public override bool PrepareForNextIteration() { + this.LearnQValues(); this.ExecutionPath.Clear(); this.PreviousOperation = 0; this.Epochs++; @@ -361,50 +358,18 @@ private void InitializeIntegerChoiceQValues(int state, int maxValue) } } - private readonly HashSet _visitedTimelines = new(); - private readonly List> _savedTimelines = new(); - private int ComputeDiversity(int timeline, List hash) - { - if (!_visitedTimelines.Add(timeline)) - { - return 0; - } - - if (_savedTimelines.Count == 0) - { - return 20; - } - - var maxSim = int.MinValue; - foreach (var record in _savedTimelines) - { - var similarity = 0; - for (int i = 0; i < hash.Count; i++) - { - if (hash[i] == record[i]) - { - similarity += 1; - } - } - - maxSim = Math.Max(maxSim, similarity); - } - - - return (hash.Count - maxSim) * 10 + 20; - } - - public void ObserveRunningResults(TimelineObserver timelineObserver) + /// + /// Learn Q values using data from the current execution. + /// + private void LearnQValues() { - var timelineHash = timelineObserver.GetTimelineHash(); - var timelineMinhash = timelineObserver.GetTimelineMinhash(); - - int priority = 1; - + var pathBuilder = new StringBuilder(); + int idx = 0; var node = this.ExecutionPath.First; while (node != null && node.Next != null) { + pathBuilder.Append($"{node.Value.op},"); var (_, _, state) = node.Value; var (nextOp, nextType, nextState) = node.Next.Value; @@ -421,8 +386,8 @@ public void ObserveRunningResults(TimelineObserver timelineObserver) // Compute the reward. Program states that are visited with higher frequency result into lesser rewards. var freq = this.TransitionFrequencies[nextState]; - double reward = ((nextType == AsyncOperationType.InjectFailure ? - this.FailureInjectionReward : this.BasicActionReward) * freq) / priority; + double reward = (nextType == AsyncOperationType.InjectFailure ? + this.FailureInjectionReward : this.BasicActionReward) * freq; if (reward > 0) { // The reward has underflowed. @@ -442,16 +407,8 @@ public void ObserveRunningResults(TimelineObserver timelineObserver) (this.LearningRate * (reward + (this.Gamma * maxQ))); node = node.Next; + idx++; } } - - public int TotalSavedInputs() - { - return 0; - } - - public void DumpStats(TextWriter writer) - { - } } } \ No newline at end of file From b4837daddd56598e99138831b5b26283bf6be1a0 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 17 Oct 2024 15:12:51 +0800 Subject: [PATCH 17/21] Refactor. --- .../{Object => }/ControlledRandom.cs | 0 .../Feedback/Generator/IGenerator.cs | 12 ---------- .../Feedback/Generator/IInputGenerator.cs | 22 ------------------- .../Feedback/Generator/IScheduleGenerator.cs | 18 --------------- .../Generator/{Object => }/RandomChoices.cs | 0 .../Feedback/Generator/{Mutator => }/Utils.cs | 0 6 files changed, 52 deletions(-) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/{Object => }/ControlledRandom.cs (100%) delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs delete mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/{Object => }/RandomChoices.cs (100%) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/{Mutator => }/Utils.cs (100%) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/ControlledRandom.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ControlledRandom.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/ControlledRandom.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/ControlledRandom.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs deleted file mode 100644 index 8face2fa56..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IGenerator.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace PChecker.Generator; - -public interface IGenerator -{ - /// - /// Mutate the current generator and create a new one. - /// - /// A new generator. - T Mutate(); - - T New(); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs deleted file mode 100644 index a8ce3b097f..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IInputGenerator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace PChecker.Generator; - -public interface IInputGenerator : IGenerator -{ - - /// - /// Returns a non-negative random number. - /// - int Next(); - - /// - /// Returns a non-negative random number less than maxValue. - /// - /// Exclusive upper bound - int Next(int maxValue); - - /// - /// Returns a random floating-point number that is greater - /// than or equal to 0.0, and less than 1.0. - /// - double NextDouble(); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs deleted file mode 100644 index 4850f55ef6..0000000000 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/IScheduleGenerator.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using PChecker.SystematicTesting.Operations; - -namespace PChecker.Generator; - -internal interface IScheduleGenerator: IGenerator -{ - /// - /// Get the next scheduled operation. - /// - /// All enabled operations. - /// Current operation. - /// Next enabled operation. - public AsyncOperation? NextRandomOperation(List enabledOperations, AsyncOperation current); - - - public void PrepareForNextInput(); -} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomChoices.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Object/RandomChoices.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/RandomChoices.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/Utils.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Utils.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Mutator/Utils.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Generator/Utils.cs From 2eda0405e49b61168d6f131d8736cae7a07f2c4d Mon Sep 17 00:00:00 2001 From: ChristineZh0u <48167738+ChristineZh0u@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:00:46 -0700 Subject: [PATCH 18/21] Added VectorTime and BehavioralObserver class for feedback strategy (#799) Co-authored-by: Christine Zhou --- .../CheckerCore/Runtime/Events/EventInfo.cs | 5 +- .../StateMachines/EventQueues/EventQueue.cs | 4 +- .../Runtime/StateMachines/StateMachine.cs | 13 ++ .../CheckerCore/Runtime/VectorTime.cs | 124 +++++++++++++ .../SystematicTesting/ControlledRuntime.cs | 2 +- .../Feedback/Coverage/BehavioralObserver.cs | 166 ++++++++++++++++++ .../SystematicTesting/TestingEngine.cs | 1 + 7 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 Src/PChecker/CheckerCore/Runtime/VectorTime.cs create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/BehavioralObserver.cs diff --git a/Src/PChecker/CheckerCore/Runtime/Events/EventInfo.cs b/Src/PChecker/CheckerCore/Runtime/Events/EventInfo.cs index a4de780989..993ab9dae7 100644 --- a/Src/PChecker/CheckerCore/Runtime/Events/EventInfo.cs +++ b/Src/PChecker/CheckerCore/Runtime/Events/EventInfo.cs @@ -23,6 +23,8 @@ internal class EventInfo [DataMember] internal EventOriginInfo OriginInfo { get; private set; } + internal VectorTime VectorTime; + /// /// Initializes a new instance of the class. /// @@ -34,10 +36,11 @@ internal EventInfo(Event e) /// /// Initializes a new instance of the class. /// - internal EventInfo(Event e, EventOriginInfo originInfo) + internal EventInfo(Event e, EventOriginInfo originInfo, VectorTime v) : this(e) { OriginInfo = originInfo; + VectorTime = new VectorTime(v); } } } \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/Runtime/StateMachines/EventQueues/EventQueue.cs b/Src/PChecker/CheckerCore/Runtime/StateMachines/EventQueues/EventQueue.cs index 7db5bb7440..569c082438 100644 --- a/Src/PChecker/CheckerCore/Runtime/StateMachines/EventQueues/EventQueue.cs +++ b/Src/PChecker/CheckerCore/Runtime/StateMachines/EventQueues/EventQueue.cs @@ -157,7 +157,7 @@ public EnqueueStatus Enqueue(Event e, EventInfo info) // A default event handler exists. var stateName = StateMachine.CurrentState.GetType().Name; var eventOrigin = new EventOriginInfo(StateMachine.Id, StateMachine.GetType().FullName, stateName); - return (DequeueStatus.Default, DefaultEvent.Instance, new EventInfo(DefaultEvent.Instance, eventOrigin)); + return (DequeueStatus.Default, DefaultEvent.Instance, new EventInfo(DefaultEvent.Instance, eventOrigin, StateMachine.VectorTime)); } /// @@ -209,7 +209,7 @@ public void RaiseEvent(Event e) { var stateName = StateMachine.CurrentState.GetType().Name; var eventOrigin = new EventOriginInfo(StateMachine.Id, StateMachine.GetType().FullName, stateName); - var info = new EventInfo(e, eventOrigin); + var info = new EventInfo(e, eventOrigin, StateMachine.VectorTime); RaisedEvent = (e, info); StateMachineManager.OnRaiseEvent(e, info); } diff --git a/Src/PChecker/CheckerCore/Runtime/StateMachines/StateMachine.cs b/Src/PChecker/CheckerCore/Runtime/StateMachines/StateMachine.cs index 0c2ae5701a..6f6019b443 100644 --- a/Src/PChecker/CheckerCore/Runtime/StateMachines/StateMachine.cs +++ b/Src/PChecker/CheckerCore/Runtime/StateMachines/StateMachine.cs @@ -54,6 +54,11 @@ public abstract class StateMachine /// private protected IEventQueue Inbox; + /// + /// Keeps track of state machine's current vector time. + /// + public VectorTime VectorTime; + /// /// Cache of state machine types to a map of action names to action declarations. /// @@ -295,6 +300,7 @@ internal void Configure(ControlledRuntime runtime, StateMachineId id, IStateMach Id = id; Manager = manager; Inbox = inbox; + VectorTime = new VectorTime(Id); } /// @@ -535,6 +541,9 @@ public void SendEvent(PMachineValue target, Event ev) Assert(target.Permissions.Contains(ev.GetType().Name), $"Event {ev.GetType().Name} is not in the permissions set of the target machine"); AnnounceInternal(ev); + // Update vector clock + VectorTime.Increment(); + BehavioralObserver.AddToCurrentTimeline(ev, BehavioralObserver.EventType.SEND, VectorTime); Runtime.SendEvent(target.Id, ev, this); } @@ -590,6 +599,10 @@ internal async Task RunEventHandlerAsync() if (status is DequeueStatus.Success) { + // Update state machine vector clock + VectorTime.Merge(info.VectorTime); + BehavioralObserver.AddToCurrentTimeline(e, BehavioralObserver.EventType.DEQUEUE, VectorTime); + // Notify the runtime for a new event to handle. This is only used // during bug-finding and operation bounding, because the runtime // has to schedule an state machine when a new operation is dequeued. diff --git a/Src/PChecker/CheckerCore/Runtime/VectorTime.cs b/Src/PChecker/CheckerCore/Runtime/VectorTime.cs new file mode 100644 index 0000000000..02359e8921 --- /dev/null +++ b/Src/PChecker/CheckerCore/Runtime/VectorTime.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using PChecker.Runtime.StateMachines; +using PChecker.IO.Logging; + +public class VectorTime +{ + // Dictionary that uses StateMachineId as the key and stores the logical clock as the value + public Dictionary Clock { get; private set; } + + // The ID of the current state machine + + private StateMachineId stateMachineId; + + public VectorTime(StateMachineId stateMachineId) + { + this.stateMachineId = stateMachineId; + Clock = new Dictionary(); + Clock[stateMachineId] = 0; // Initialize the clock for this state machine + } + + // Clone constructor (creates a snapshot of the vector clock) + public VectorTime(VectorTime other) + { + Clock = new Dictionary(other.Clock); + } + + // Increment the logical clock for this state machine + public void Increment() + { + Clock[stateMachineId]++; + } + + // Merge another vector clock into this one + public void Merge(VectorTime otherTime) + { + foreach (var entry in otherTime.Clock) + { + StateMachineId otherMachineId = entry.Key; + int otherTimestamp = entry.Value; + + if (Clock.ContainsKey(otherMachineId)) + { + // Take the maximum timestamp for each state machine + Clock[otherMachineId] = Math.Max(Clock[otherMachineId], otherTimestamp); + } + else + { + // Add the state machine's timestamp if it doesn't exist in this time + Clock[otherMachineId] = otherTimestamp; + } + } + } + + // Compare this vector clock to another for sorting purposes + // Rturn value: -1 = This vector clock happens after the other, 1 = This vector clock happens before the other, + // 0 = Clocks are equal or concurrent + public int CompareTo(VectorTime other) + { + bool atLeastOneLess = false; + bool atLeastOneGreater = false; + + foreach (var machineId in Clock.Keys) + { + int thisTime = Clock[machineId]; + int otherTime = other.Clock.ContainsKey(machineId) ? other.Clock[machineId] : 0; + + if (thisTime < otherTime) + { + atLeastOneLess = true; + } + else if (thisTime > otherTime) + { + atLeastOneGreater = true; + } + if (atLeastOneLess && atLeastOneGreater) + { + return 0; + } + } + if (atLeastOneLess && !atLeastOneGreater) + { + return -1; + } + if (atLeastOneGreater && !atLeastOneLess) + { + return 1; + } + return 0; + } + + + public override string ToString() + { + var elements = new List(); + foreach (var entry in Clock) + { + elements.Add($"StateMachine {entry.Key.Name}: {entry.Value}"); + } + return $"[{string.Join(", ", elements)}]"; + } + + public override bool Equals(object obj) + { + if (obj is VectorTime other) + { + return Clock.OrderBy(x => x.Key).SequenceEqual(other.Clock.OrderBy(x => x.Key)); + } + return false; + } + + public override int GetHashCode() + { + int hash = 17; + foreach (var entry in Clock) + { + hash = hash * 31 + entry.Key.GetHashCode(); + hash = hash * 31 + entry.Value.GetHashCode(); + } + return hash; + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs index 23327d0a8d..1c6b814453 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/ControlledRuntime.cs @@ -649,7 +649,7 @@ private EnqueueStatus EnqueueEvent(StateMachine stateMachine, Event e, StateMach var originInfo = new EventOriginInfo(sender.Id, sender.GetType().FullName, sender.CurrentState.GetType().Name); - var eventInfo = new EventInfo(e, originInfo); + var eventInfo = new EventInfo(e, originInfo, sender.VectorTime); LogWriter.LogSendEvent(stateMachine.Id, sender.Id.Name, sender.Id.Type, sender.CurrentStateName, e, isTargetHalted: false); diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/BehavioralObserver.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/BehavioralObserver.cs new file mode 100644 index 0000000000..35a3353d0a --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/Coverage/BehavioralObserver.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.IO; +using PChecker.IO.Logging; +using PChecker.Runtime.Events; + +namespace PChecker.Runtime; + +public class BehavioralObserver +{ + private static HashSet<(VectorTime, Event, EventType)> CurrTimeline = new HashSet<(VectorTime, Event, EventType)>(); + private static List AllTimeline = new List(); + private static TextWriter Logger = new ConsoleLogger(); + // MinHash object with 100 hash functions, check how many of these 100 values are identical between the two sets + private static MinHash minHash = new MinHash(100); + + + public enum EventType + { + SEND, DEQUEUE + } + + public BehavioralObserver() + { + CurrTimeline = new HashSet<(VectorTime, Event, EventType)>(); + } + + public static void AddToCurrentTimeline(Event e, EventType t, VectorTime vectorTime) + { + CurrTimeline.Add((new VectorTime(vectorTime), e, t)); + } + + public static int CalculateDiff(int[] currentSignature, int[] otherSignature) + { + double similarity = minHash.ComputeSimilarity(currentSignature, otherSignature); + double differenceScore = 1 - similarity; + return (int)(differenceScore * 100); // Scale the difference score (e.g., multiply by 100) + } + + public static int GetUniqueScore(int[] currentSignature) + { + int scoreSum = 0; + foreach (var otherSignature in AllTimeline) + { + int score = CalculateDiff(currentSignature, otherSignature); + if (score == 0) + { + return 0; + } + scoreSum += score; + } + return scoreSum/AllTimeline.Count; + } + + public static void PrintTimeline(List<(VectorTime, Event, EventType)> timeline) + { + Logger.WriteLine("Global state of all machines:"); + foreach (var entry in timeline) + { + Logger.WriteLine($"Machine {entry}"); + } + } + + public static List<(VectorTime, Event, EventType)> SortByVectorClock() + { + List<(VectorTime, Event, EventType)> sortedTimeline = new List<(VectorTime, Event, EventType)>(CurrTimeline); + + // Sort by event name then vector time + sortedTimeline.Sort((x, y) => x.Item2.ToString().CompareTo(y.Item2.ToString())); + sortedTimeline.Sort((x, y) => x.Item1.CompareTo(y.Item1)); + + return sortedTimeline; + } + + public static void NextIter() + { + if (CurrTimeline.Count == 0) + { + return; + } + List<(VectorTime, Event, EventType)> SortedCurrTimeline = SortByVectorClock(); + int[] currSignature = minHash.ComputeMinHashSignature(SortedCurrTimeline); + if (AllTimeline.Count == 0) + { + AllTimeline.Add(currSignature); + return; + } + int uniqueScore = GetUniqueScore(currSignature); + Logger.WriteLine($"----**** UniquenessScore: {uniqueScore}"); + if (uniqueScore != 0) + { + AllTimeline.Add(currSignature); + } + CurrTimeline = new HashSet<(VectorTime, Event, EventType)>(); + } +} + +public class MinHash +{ + private int numHashFunctions; // Number of hash functions to use + private List> hashFunctions; // List of hash functions + + public MinHash(int numHashFunctions) + { + this.numHashFunctions = numHashFunctions; + this.hashFunctions = new List>(); + + System.Random rand = new System.Random(); + for (int i = 0; i < numHashFunctions; i++) + { + int a = rand.Next(); + int b = rand.Next(); + hashFunctions.Add(x => a * x + b); + } + } + + // Create a MinHash signature for a given set + public int[] ComputeMinHashSignature(List<(VectorTime, Event, BehavioralObserver.EventType)> set) + { + int[] signature = new int[numHashFunctions]; + + for (int i = 0; i < numHashFunctions; i++) + { + signature[i] = int.MaxValue; + } + foreach (var element in set) + { + int elementHash = ComputeElementHash(element); + + for (int i = 0; i < numHashFunctions; i++) + { + int hashedValue = hashFunctions[i](elementHash); + if (hashedValue < signature[i]) + { + signature[i] = hashedValue; + } + } + } + return signature; + } + + // Compute a composite hash for the (VectorTime, Event, EventType) tuple + private int ComputeElementHash((VectorTime, Event, BehavioralObserver.EventType) element) + { + int hash1 = element.Item1.GetHashCode(); + int hash2 = element.Item2.GetHashCode(); + int hash3 = element.Item3.GetHashCode(); + return hash1 ^ hash2 ^ hash3; + } + + // Compute Jaccard similarity based on MinHash signatures + public double ComputeSimilarity(int[] signature1, int[] signature2) + { + int identicalMinHashes = 0; + + for (int i = 0; i < numHashFunctions; i++) + { + if (signature1[i] == signature2[i]) + { + identicalMinHashes++; + } + } + return (double)identicalMinHashes / numHashFunctions; + } +} + diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index d0bfe9974d..da7952ce74 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -542,6 +542,7 @@ private void RegisterObservers(ControlledRuntime runtime) /// private void RunNextIteration(int schedule) { + BehavioralObserver.NextIter(); if (!IsReplayModeEnabled && ShouldPrintIteration(schedule + 1)) { Logger.WriteLine($"..... Schedule #{schedule + 1}"); From 5e306b4d6f2bce0323afd76383bc7192de14cb8a Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 18 Oct 2024 15:20:05 +0800 Subject: [PATCH 19/21] Revert changes in PCTStrategy. --- .../{Probabilistic => Feedback}/IScheduler.cs | 0 .../PCTScheduler.cs | 0 .../POSScheduler.cs | 0 .../ScheduleAndInputStrategy.cs | 0 .../Strategies/Probabilistic/PCTStrategy.cs | 278 ++++++++++++++++++ .../SystematicTesting/TestingEngine.cs | 15 +- 6 files changed, 282 insertions(+), 11 deletions(-) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/{Probabilistic => Feedback}/IScheduler.cs (100%) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/{Probabilistic => Feedback}/PCTScheduler.cs (100%) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/{Probabilistic => Feedback}/POSScheduler.cs (100%) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/{Probabilistic => Feedback}/ScheduleAndInputStrategy.cs (100%) create mode 100644 Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/IScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IScheduler.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/IScheduler.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/IScheduler.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/PCTScheduler.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTScheduler.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/PCTScheduler.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/POSScheduler.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/POSScheduler.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/POSScheduler.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/ScheduleAndInputStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ScheduleAndInputStrategy.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/ScheduleAndInputStrategy.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/ScheduleAndInputStrategy.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs new file mode 100644 index 0000000000..825a6d4be5 --- /dev/null +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs @@ -0,0 +1,278 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using PChecker.IO.Debugging; +using PChecker.Random; +using PChecker.SystematicTesting.Operations; + +namespace PChecker.SystematicTesting.Strategies.Probabilistic +{ + /// + /// A priority-based probabilistic scheduling strategy. + /// + /// + /// This strategy is described in the following paper: + /// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/asplos277-pct.pdf + /// + internal sealed class PCTStrategy : ISchedulingStrategy + { + /// + /// Random value generator. + /// + private readonly IRandomValueGenerator RandomValueGenerator; + + /// + /// The maximum number of steps to schedule. + /// + private readonly int MaxScheduledSteps; + + /// + /// The number of scheduled steps. + /// + private int ScheduledSteps; + + /// + /// Max number of priority switch points. + /// + private readonly int MaxPrioritySwitchPoints; + + /// + /// Approximate length of the schedule across all schedules. + /// + private int ScheduleLength; + + /// + /// List of prioritized operations. + /// + private readonly List PrioritizedOperations; + + /// + /// Set of priority change points. + /// + private readonly SortedSet PriorityChangePoints; + + /// + /// Initializes a new instance of the class. + /// + public PCTStrategy(int maxSteps, int maxPrioritySwitchPoints, IRandomValueGenerator random) + { + RandomValueGenerator = random; + MaxScheduledSteps = maxSteps; + ScheduledSteps = 0; + ScheduleLength = 0; + MaxPrioritySwitchPoints = maxPrioritySwitchPoints; + PrioritizedOperations = new List(); + PriorityChangePoints = new SortedSet(); + } + + /// + public bool GetNextOperation(AsyncOperation current, IEnumerable ops, out AsyncOperation next) + { + next = null; + var enabledOperations = ops.Where(op => op.Status is AsyncOperationStatus.Enabled).ToList(); + if (enabledOperations.Count == 0) + { + return false; + } + + var highestEnabledOp = GetPrioritizedOperation(enabledOperations, current); + if (next is null) + { + next = highestEnabledOp; + } + + ScheduledSteps++; + + return true; + } + + /// + public bool GetNextBooleanChoice(AsyncOperation current, int maxValue, out bool next) + { + next = false; + if (RandomValueGenerator.Next(maxValue) == 0) + { + next = true; + } + + ScheduledSteps++; + + return true; + } + + /// + public bool GetNextIntegerChoice(AsyncOperation current, int maxValue, out int next) + { + next = RandomValueGenerator.Next(maxValue); + ScheduledSteps++; + return true; + } + + /// + public bool PrepareForNextIteration() + { + ScheduleLength = Math.Max(ScheduleLength, ScheduledSteps); + ScheduledSteps = 0; + + PrioritizedOperations.Clear(); + PriorityChangePoints.Clear(); + + var range = new List(); + for (var idx = 0; idx < ScheduleLength; idx++) + { + range.Add(idx); + } + + foreach (var point in Shuffle(range).Take(MaxPrioritySwitchPoints)) + { + PriorityChangePoints.Add(point); + } + + return true; + } + + /// + public int GetScheduledSteps() => ScheduledSteps; + + /// + public bool HasReachedMaxSchedulingSteps() + { + if (MaxScheduledSteps == 0) + { + return false; + } + + return ScheduledSteps >= MaxScheduledSteps; + } + + /// + public bool IsFair() => false; + + /// + public string GetDescription() + { + var text = $"pct[priority change points '{MaxPrioritySwitchPoints}' [" + + string.Join(", ", PriorityChangePoints.ToArray()) + + "], seed '" + RandomValueGenerator.Seed + "']"; + return text; + } + + /// + /// Returns the prioritized operation. + /// + private AsyncOperation GetPrioritizedOperation(List ops, AsyncOperation current) + { + if (PrioritizedOperations.Count == 0) + { + PrioritizedOperations.Add(current); + } + + foreach (var op in ops.Where(op => !PrioritizedOperations.Contains(op))) + { + var mIndex = RandomValueGenerator.Next(PrioritizedOperations.Count) + 1; + PrioritizedOperations.Insert(mIndex, op); + Debug.WriteLine(" Detected new operation '{0}' at index '{1}'.", op.Id, mIndex); + } + + if (PriorityChangePoints.Contains(ScheduledSteps)) + { + if (ops.Count == 1) + { + MovePriorityChangePointForward(); + } + else + { + var priority = GetHighestPriorityEnabledOperation(ops); + PrioritizedOperations.Remove(priority); + PrioritizedOperations.Add(priority); + Debug.WriteLine(" Operation '{0}' changes to lowest priority.", priority); + } + } + + var prioritizedSchedulable = GetHighestPriorityEnabledOperation(ops); + if (Debug.IsEnabled) + { + Debug.WriteLine(" Prioritized schedulable '{0}'.", prioritizedSchedulable); + Debug.Write(" Priority list: "); + for (var idx = 0; idx < PrioritizedOperations.Count; idx++) + { + if (idx < PrioritizedOperations.Count - 1) + { + Debug.Write("'{0}', ", PrioritizedOperations[idx]); + } + else + { + Debug.WriteLine("'{0}'.", PrioritizedOperations[idx]); + } + } + } + + return ops.First(op => op.Equals(prioritizedSchedulable)); + } + + /// + /// Returns the highest-priority enabled operation. + /// + private AsyncOperation GetHighestPriorityEnabledOperation(IEnumerable choices) + { + AsyncOperation prioritizedOp = null; + foreach (var entity in PrioritizedOperations) + { + if (choices.Any(m => m == entity)) + { + prioritizedOp = entity; + break; + } + } + + return prioritizedOp; + } + + /// + /// Shuffles the specified list using the Fisher-Yates algorithm. + /// + private IList Shuffle(IList list) + { + var result = new List(list); + for (var idx = result.Count - 1; idx >= 1; idx--) + { + var point = RandomValueGenerator.Next(ScheduleLength); + var temp = result[idx]; + result[idx] = result[point]; + result[point] = temp; + } + + return result; + } + + /// + /// Moves the current priority change point forward. This is a useful + /// optimization when a priority change point is assigned in either a + /// sequential execution or a nondeterministic choice. + /// + private void MovePriorityChangePointForward() + { + PriorityChangePoints.Remove(ScheduledSteps); + var newPriorityChangePoint = ScheduledSteps + 1; + while (PriorityChangePoints.Contains(newPriorityChangePoint)) + { + newPriorityChangePoint++; + } + + PriorityChangePoints.Add(newPriorityChangePoint); + Debug.WriteLine(" Moving priority change to '{0}'.", newPriorityChangePoint); + } + + /// + public void Reset() + { + ScheduleLength = 0; + ScheduledSteps = 0; + PrioritizedOperations.Clear(); + PriorityChangePoints.Clear(); + } + } +} \ No newline at end of file diff --git a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs index da7952ce74..32a33cfcec 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/TestingEngine.cs @@ -286,16 +286,12 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo } else if (checkerConfiguration.SchedulingStrategy is "random") { - var scheduler = new RandomScheduler(RandomValueGenerator); - Strategy = new ScheduleAndInputStrategy(checkerConfiguration.MaxFairSchedulingSteps, - RandomValueGenerator, scheduler); + Strategy = new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); } else if (checkerConfiguration.SchedulingStrategy is "pct") { - var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, + Strategy = new PCTStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, checkerConfiguration.StrategyBound, RandomValueGenerator); - Strategy = new ScheduleAndInputStrategy(checkerConfiguration.MaxUnfairSchedulingSteps, - RandomValueGenerator, scheduler); } else if (checkerConfiguration.SchedulingStrategy is "pos") { @@ -306,11 +302,8 @@ private TestingEngine(CheckerConfiguration checkerConfiguration, TestMethodInfo else if (checkerConfiguration.SchedulingStrategy is "fairpct") { var prefixLength = checkerConfiguration.MaxUnfairSchedulingSteps; - var scheduler = new PCTScheduler(checkerConfiguration.StrategyBound, 0, - RandomValueGenerator); - var prefixStrategy = new ScheduleAndInputStrategy(prefixLength, RandomValueGenerator, scheduler); - var suffixStrategy = - new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); + var prefixStrategy = new PCTStrategy(prefixLength, checkerConfiguration.StrategyBound, RandomValueGenerator); + var suffixStrategy = new RandomStrategy(checkerConfiguration.MaxFairSchedulingSteps, RandomValueGenerator); Strategy = new ComboStrategy(prefixStrategy, suffixStrategy); } else if (checkerConfiguration.SchedulingStrategy is "probabilistic") From 91cd7176ea2fc319cf649d9edaff4db3451c3dbe Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 18 Oct 2024 15:23:37 +0800 Subject: [PATCH 20/21] Revert changes in Probabilistic folder. --- .../{Probabilistic => Feedback}/RandomScheduler.cs | 0 .../Strategies/Probabilistic/PCTStrategy.cs | 1 - .../Strategies/Probabilistic/RandomStrategy.cs | 6 +----- 3 files changed, 1 insertion(+), 6 deletions(-) rename Src/PChecker/CheckerCore/SystematicTesting/Strategies/{Probabilistic => Feedback}/RandomScheduler.cs (100%) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomScheduler.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/RandomScheduler.cs similarity index 100% rename from Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomScheduler.cs rename to Src/PChecker/CheckerCore/SystematicTesting/Strategies/Feedback/RandomScheduler.cs diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs index 825a6d4be5..21cee4414c 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs @@ -1,4 +1,3 @@ -// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs index eefa755621..7bd654b618 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/RandomStrategy.cs @@ -48,11 +48,7 @@ public virtual bool GetNextOperation(AsyncOperation current, IEnumerable 1) { - idx = RandomValueGenerator.Next(enabledOperations.Count); - } - + var idx = RandomValueGenerator.Next(enabledOperations.Count); next = enabledOperations[idx]; ScheduledSteps++; From 3ee77e317a31f417d575f9cbb61cc8ab50903b6a Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 18 Oct 2024 15:25:49 +0800 Subject: [PATCH 21/21] revert change. --- .../SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs index 21cee4414c..3ffdc47c3c 100644 --- a/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs +++ b/Src/PChecker/CheckerCore/SystematicTesting/Strategies/Probabilistic/PCTStrategy.cs @@ -1,3 +1,4 @@ +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System;