From 634f871a7b9153a9e4503fe05e1303bd8a54bcc1 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sat, 11 Sep 2021 15:58:10 +0300 Subject: [PATCH 01/88] Add discover constants recursive function --- VSharp.SILI.Core/Terms.fs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index 445709d14..c23cd32c1 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -642,7 +642,26 @@ module internal Terms = | _ -> () Seq.iter (iter addConstant) terms result :> ISet - + + let discoverConstantsRec term = + let result = HashSet() + let rec discoverConstantsInner term k = + let k _ = k() + match term.term with + | Nop | Concrete _ | HeapRef _ | Ref _ | Ptr(_, _, None) -> k() + | Constant _ -> + result.Add term |> ignore + k() + | Expression(_, operands, _) -> + Cps.List.mapk discoverConstantsInner operands k + | Struct(fields, _) -> + Cps.Seq.mapk discoverConstantsInner (PersistentDict.values fields) k + | Ptr(_, _, Some(term)) -> discoverConstantsInner term k + | Union(guardedTerms) -> + let guards, terms = List.unzip guardedTerms + Cps.Seq.mapk discoverConstantsInner (List.append guards terms) k + discoverConstantsInner term id + result :> ISet let private foldFields isStatic folder acc typ = let dotNetType = Types.toDotNetType typ From e110c7e5af84f1367fa670752b2de78ee479324e Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sat, 11 Sep 2021 19:13:28 +0300 Subject: [PATCH 02/88] Try to implement persistent union find --- VSharp.Utils/PersistentUnionFind.fs | 37 +++++++++++++++++++++++++++++ VSharp.Utils/VSharp.Utils.fsproj | 1 + 2 files changed, 38 insertions(+) create mode 100644 VSharp.Utils/PersistentUnionFind.fs diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs new file mode 100644 index 000000000..14d1c50db --- /dev/null +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -0,0 +1,37 @@ +module VSharp.Utils.PersistentUnionFind + +open VSharp + +type public puf<'a> when 'a : equality = + private {impl : pdict<'a, 'a>} + +module public PersistentUnionFind = + + let public empty<'a when 'a : equality> : puf<'a> = {impl = PersistentDict.empty} + + let rec public tryFind a puf = + let tryFindInternal pdict a = + try + PersistentDict.find pdict a |> Some + with + _ -> None + match tryFindInternal puf.impl a with + | Some b when a = b -> Some a + | Some b -> tryFind b puf + | None -> None + + let public union a b puf = + let aParentOption = tryFind a puf + let bParentOption = tryFind b puf + match aParentOption, bParentOption with + | Some aParent, Some bParent when aParent <> bParent -> + {impl = PersistentDict.add aParent bParent puf.impl} + | _ -> puf + + let public add puf a = + match tryFind a puf with + | Some _ -> puf + | None -> {impl = PersistentDict.add a a puf.impl} + + + \ No newline at end of file diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 9f85d3f15..4efd19a71 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -31,6 +31,7 @@ + From b537708e43216c0f9889d920dc3461d0a516551d Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sat, 11 Sep 2021 21:00:58 +0300 Subject: [PATCH 03/88] Fix discover constants to use pset --- VSharp.SILI.Core/PathConditionIndependent.fs | 54 ++++++++++++++++++++ VSharp.SILI.Core/Terms.fs | 20 +++----- VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 1 + VSharp.Utils/PersistentUnionFind.fs | 19 +++---- 4 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 VSharp.SILI.Core/PathConditionIndependent.fs diff --git a/VSharp.SILI.Core/PathConditionIndependent.fs b/VSharp.SILI.Core/PathConditionIndependent.fs new file mode 100644 index 000000000..7689d0531 --- /dev/null +++ b/VSharp.SILI.Core/PathConditionIndependent.fs @@ -0,0 +1,54 @@ +module VSharp.SILI.Core.PathConditionIndependent + +open VSharp +open VSharp.Core +open VSharp.Utils.PersistentUnionFind + +type pathConditionIndependent = + private {constraints : pset; constants : pUnionFind; constraintConstants : pdict} + +// Invariants: +// - PCI does not contain True +// - if PCI contains False then False is the only element in PCI + +module internal PCI = + + let public empty = + {constraints = PersistentSet.empty + constants = PersistentUnionFind.empty + constraintConstants = PersistentDict.empty} + + let public isEmpty pc = PersistentSet.isEmpty pc.constraints + + let public toSeq pc = PersistentSet.toSeq pc.constraints + + let private falsePC = + {constraints = PersistentSet.add PersistentSet.empty False + constants = PersistentUnionFind.empty + constraintConstants = PersistentDict.empty} + + let public isFalse pc = + let isFalsePC = PersistentSet.contains False pc.constraints + if isFalsePC then assert(toSeq pc |> Seq.length = 1) + isFalsePC + + let public add pc cond : pathConditionIndependent = + match cond with + | True -> pc + | False -> falsePC + | _ when isFalse pc -> falsePC + | _ when PersistentSet.contains !!cond pc.constraints -> falsePC + | _ -> + let parents = discoverConstantsRec cond |> PersistentSet.map (PersistentUnionFind.tryFind pc.constants) + falsePC + + let public mapPC mapper (pc : pathCondition) : pathCondition = + let mapAndAdd acc cond k = + let acc' = mapper cond |> add acc + if isFalse acc' then falsePC else k acc' + Cps.Seq.foldlk mapAndAdd empty (toSeq pc) id + let public mapSeq mapper (pc : pathCondition) = toSeq pc |> Seq.map mapper + + let toString pc = mapSeq toString pc |> Seq.sort |> join " /\ " + + let union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) \ No newline at end of file diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index c23cd32c1..96d09eaeb 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -644,24 +644,20 @@ module internal Terms = result :> ISet let discoverConstantsRec term = - let result = HashSet() - let rec discoverConstantsInner term k = - let k _ = k() + let rec discoverConstantsInner acc term k = match term.term with - | Nop | Concrete _ | HeapRef _ | Ref _ | Ptr(_, _, None) -> k() + | Nop | Concrete _ | HeapRef _ | Ref _ | Ptr(_, _, None) -> k acc | Constant _ -> - result.Add term |> ignore - k() + PersistentSet.add acc term |> k | Expression(_, operands, _) -> - Cps.List.mapk discoverConstantsInner operands k + Cps.List.foldlk discoverConstantsInner acc operands k | Struct(fields, _) -> - Cps.Seq.mapk discoverConstantsInner (PersistentDict.values fields) k - | Ptr(_, _, Some(term)) -> discoverConstantsInner term k + Cps.Seq.foldlk discoverConstantsInner acc (PersistentDict.values fields) k + | Ptr(_, _, Some(term)) -> discoverConstantsInner acc term k | Union(guardedTerms) -> let guards, terms = List.unzip guardedTerms - Cps.Seq.mapk discoverConstantsInner (List.append guards terms) k - discoverConstantsInner term id - result :> ISet + Cps.Seq.foldlk discoverConstantsInner acc (List.append guards terms) k + discoverConstantsInner PersistentSet.empty term id let private foldFields isStatic folder acc typ = let dotNetType = Types.toDotNetType typ diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index d42f5f2b1..a3078e04e 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -39,6 +39,7 @@ + diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index 14d1c50db..701a1f9c2 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -2,14 +2,14 @@ open VSharp -type public puf<'a> when 'a : equality = +type public pUnionFind<'a> when 'a : equality = private {impl : pdict<'a, 'a>} module public PersistentUnionFind = - let public empty<'a when 'a : equality> : puf<'a> = {impl = PersistentDict.empty} + let public empty<'a when 'a : equality> : pUnionFind<'a> = {impl = PersistentDict.empty} - let rec public tryFind a puf = + let rec public tryFind puf a = let tryFindInternal pdict a = try PersistentDict.find pdict a |> Some @@ -17,21 +17,18 @@ module public PersistentUnionFind = _ -> None match tryFindInternal puf.impl a with | Some b when a = b -> Some a - | Some b -> tryFind b puf + | Some b -> tryFind puf b | None -> None let public union a b puf = - let aParentOption = tryFind a puf - let bParentOption = tryFind b puf + let aParentOption = tryFind puf a + let bParentOption = tryFind puf b match aParentOption, bParentOption with | Some aParent, Some bParent when aParent <> bParent -> {impl = PersistentDict.add aParent bParent puf.impl} | _ -> puf let public add puf a = - match tryFind a puf with + match tryFind puf a with | Some _ -> puf - | None -> {impl = PersistentDict.add a a puf.impl} - - - \ No newline at end of file + | None -> {impl = PersistentDict.add a a puf.impl} \ No newline at end of file From 4c01d0bab2c47fb2abccc70a8fe80ec252612d5a Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 13 Sep 2021 19:45:33 +0300 Subject: [PATCH 04/88] Try to implement path condition slicing --- VSharp.SILI.Core/PathConditionIndependent.fs | 71 +++++++++++++++----- VSharp.Utils/PersistentDictionary.fs | 20 +++++- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/VSharp.SILI.Core/PathConditionIndependent.fs b/VSharp.SILI.Core/PathConditionIndependent.fs index 7689d0531..d38625a09 100644 --- a/VSharp.SILI.Core/PathConditionIndependent.fs +++ b/VSharp.SILI.Core/PathConditionIndependent.fs @@ -5,7 +5,7 @@ open VSharp.Core open VSharp.Utils.PersistentUnionFind type pathConditionIndependent = - private {constraints : pset; constants : pUnionFind; constraintConstants : pdict} + private {constants : pUnionFind; constraintsWithConstants : pdict} // Invariants: // - PCI does not contain True @@ -14,41 +14,76 @@ type pathConditionIndependent = module internal PCI = let public empty = - {constraints = PersistentSet.empty - constants = PersistentUnionFind.empty - constraintConstants = PersistentDict.empty} + {constants = PersistentUnionFind.empty + constraintsWithConstants = PersistentDict.empty} - let public isEmpty pc = PersistentSet.isEmpty pc.constraints + let public isEmpty pc = PersistentDict.isEmpty pc.constraintsWithConstants - let public toSeq pc = PersistentSet.toSeq pc.constraints + let public toSeq pc = PersistentDict.keys pc.constraintsWithConstants let private falsePC = - {constraints = PersistentSet.add PersistentSet.empty False - constants = PersistentUnionFind.empty - constraintConstants = PersistentDict.empty} + {constants = PersistentUnionFind.empty + constraintsWithConstants = PersistentDict.add False None PersistentDict.empty} let public isFalse pc = - let isFalsePC = PersistentSet.contains False pc.constraints + let isFalsePC = PersistentDict.contains False pc.constraintsWithConstants if isFalsePC then assert(toSeq pc |> Seq.length = 1) isFalsePC - + let public add pc cond : pathConditionIndependent = match cond with | True -> pc | False -> falsePC | _ when isFalse pc -> falsePC - | _ when PersistentSet.contains !!cond pc.constraints -> falsePC + | _ when PersistentDict.contains !!cond pc.constraintsWithConstants -> falsePC | _ -> - let parents = discoverConstantsRec cond |> PersistentSet.map (PersistentUnionFind.tryFind pc.constants) - falsePC - - let public mapPC mapper (pc : pathCondition) : pathCondition = + let consts = discoverConstantsRec cond + let tryHead = PersistentSet.toSeq >> Seq.tryHead + let pufWithNewConsts = + consts + |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind pc.constants t) + |> PersistentSet.fold PersistentUnionFind.add pc.constants + let pufWithMergedConsts = + consts + |> tryHead + |> function + | Some(parent) -> + PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufWithNewConsts consts + | None -> pufWithNewConsts + {constants = pufWithMergedConsts + constraintsWithConstants = + consts + |> tryHead + |> (fun head -> PersistentDict.add cond head pc.constraintsWithConstants)} + + let public mapPC mapper (pc : pathConditionIndependent) : pathConditionIndependent = let mapAndAdd acc cond k = let acc' = mapper cond |> add acc if isFalse acc' then falsePC else k acc' Cps.Seq.foldlk mapAndAdd empty (toSeq pc) id - let public mapSeq mapper (pc : pathCondition) = toSeq pc |> Seq.map mapper + let public mapSeq mapper (pc : pathConditionIndependent) = toSeq pc |> Seq.map mapper let toString pc = mapSeq toString pc |> Seq.sort |> join " /\ " - let union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) \ No newline at end of file + let union (pc1 : pathConditionIndependent) (pc2 : pathConditionIndependent) = Seq.fold add pc1 (toSeq pc2) + + let public slice pc cond : pathCondition = + PersistentDict.find pc.constraintsWithConstants cond + |> function + | Some(constant) -> PersistentUnionFind.tryFind pc.constants constant + | None -> None + |> function + | Some _ as parent -> + pc.constraintsWithConstants + |> PersistentDict.toSeq + |> Seq.filter + (function + | _, Some c -> parent = PersistentUnionFind.tryFind pc.constants c + | _ -> false) + |> Seq.map + (function + | t, Some _ -> t + | _ -> __unreachable__()) + |> PersistentSet.ofSeq + | None -> PC.empty + \ No newline at end of file diff --git a/VSharp.Utils/PersistentDictionary.fs b/VSharp.Utils/PersistentDictionary.fs index 23a5650cc..9de5d1007 100644 --- a/VSharp.Utils/PersistentDictionary.fs +++ b/VSharp.Utils/PersistentDictionary.fs @@ -40,8 +40,12 @@ module public PersistentDict = let public size (d : pdict<'a, 'b>) = d.impl.Length - let public map (keyMapper : 'a -> 'a) (valueMapper : 'b -> 'c) (d : pdict<'a, 'b>) : pdict<'a, 'c> = + let public map (keyMapper : 'a -> 'd) (valueMapper : 'b -> 'c) (d : pdict<'a, 'b>) : pdict<'d, 'c> = d |> toSeq |> Seq.map (fun (k, v) -> (keyMapper k, valueMapper v)) |> ofSeq + + let public filterKeys (filter : 'a -> bool) (d : pdict<'a, 'b>) : pdict<'a, 'b> = + d |> toSeq |> Seq.filter (fun (k, _) -> filter k) |> ofSeq + let public fold folder state (d : pdict<'a, 'b>) = d |> toSeq |> Seq.fold (fun state (k, v) -> folder state k v) state let public forall predicate (d : pdict<'a, 'b>) = @@ -106,6 +110,9 @@ module PersistentSet = let public isEmpty (d : pset<'a>) = PersistentDict.isEmpty d let public toSeq (d : pset<'a>) = PersistentDict.keys d + + let public ofSeq (s : seq<'a>) : pset<'a> = + s |> Seq.map (fun k -> k, 0) |> PersistentDict.ofSeq let public contains (key : 'a) (d : pset<'a>) = PersistentDict.contains key d @@ -119,9 +126,16 @@ module PersistentSet = d |> toSeq |> Seq.fold folder state let public forall predicate (d : pset<'a>) = d |> toSeq |> Seq.forall predicate - let public map (mapper : 'a -> 'a) (d : pset<'a>) : pset<'a> = + + let public map (mapper : 'a -> 'b) (d : pset<'a>) : pset<'b> = PersistentDict.map mapper id d - + + let public filter (filter : 'a -> bool) (d : pset<'a>) : pset<'a> = + PersistentDict.filterKeys filter d + + let public choose (chooser : 'a -> 'b option) (d : pset<'a>) : pset<'b> = + d |> toSeq |> Seq.choose chooser |> ofSeq + let subtract (s1 : pset<'a>) (s2 : pset<'a>) = Seq.fold remove s1 (toSeq s2) let union (s1 : pset<'a>) (s2 : pset<'a>) = From 8a823f502d62df6e6d8f23869254506813c08337 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 1 Oct 2021 15:19:21 +0300 Subject: [PATCH 05/88] Implement IndependentWith method for constant sources --- VSharp.SILI.Core/Copying.fs | 5 +++++ VSharp.SILI.Core/Memory.fs | 26 ++++++++++++++++++++++++++ VSharp.SILI.Core/Pointers.fs | 3 ++- VSharp.SILI.Core/Terms.fs | 1 + VSharp.SILI.Core/TypeCasting.fs | 4 ++++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/VSharp.SILI.Core/Copying.fs b/VSharp.SILI.Core/Copying.fs index b88f21c47..dc2678b4c 100644 --- a/VSharp.SILI.Core/Copying.fs +++ b/VSharp.SILI.Core/Copying.fs @@ -13,6 +13,11 @@ module internal Copying = interface INonComposableSymbolicConstantSource with override x.SubTerms = seq[] :> term seq override x.Time = VectorTime.zero + override x.IndependentWith otherSource = + match otherSource with + | :? symbolicArrayIndexSource as otherIndex -> + x.lowerBound <> otherIndex.lowerBound || x.upperBound <> otherIndex.upperBound + | _ -> true let private makeArrayIndexConstant state lowerBound upperBound = let source = {lowerBound = lowerBound; upperBound = upperBound} diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 15c85ddd9..30623cb2a 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -151,6 +151,10 @@ module internal Memory = | Some time -> time | None -> internalfailf "Requesting time of primitive stack location %O" x.key override x.TypeOfLocation = x.key.TypeOfLocation + override x.IndependentWith otherSource = + match otherSource with + | :? stackReading as otherReading -> x <> otherReading + | _ -> true [] type private heapReading<'key, 'reg when 'key : equality and 'key :> IMemoryKey<'key, 'reg> and 'reg : equality and 'reg :> IRegion<'reg>> = @@ -159,6 +163,12 @@ module internal Memory = override x.SubTerms = Seq.empty override x.Time = x.time override x.TypeOfLocation = x.picker.sort.TypeOfLocation + override x.IndependentWith otherSource = + match otherSource with + | :? heapReading<'key, 'reg> as otherReading -> + let rootRegions hr = match hr.memoryObject.updates with | Node dict -> PersistentDict.keys dict + rootRegions x |> Seq.forall (fun reg1 -> Seq.forall (fun reg2 -> reg1.CompareTo reg2 = Disjoint) (rootRegions otherReading)) + | _ -> true let (|HeapReading|_|) (src : IMemoryAccessConstantSource) = match src with @@ -208,6 +218,11 @@ module internal Memory = override x.SubTerms = x.baseSource.SubTerms override x.Time = x.baseSource.Time override x.TypeOfLocation = fromDotNetType x.field.typ + override x.IndependentWith otherSource = + match otherSource with + | :? structField as otherField -> + x.field <> otherField.field || x.baseSource.IndependentWith otherField.baseSource + | _ -> true let (|StructFieldSource|_|) (src : IMemoryAccessConstantSource) = match src with @@ -221,6 +236,10 @@ module internal Memory = override x.SubTerms = x.baseSource.SubTerms override x.Time = x.baseSource.Time override x.TypeOfLocation = x.baseSource.TypeOfLocation + override x.IndependentWith otherSource = + match otherSource with + | :? heapAddressSource as otherAddress -> x.baseSource.IndependentWith otherAddress.baseSource + | _ -> true let (|HeapAddressSource|_|) (src : IMemoryAccessConstantSource) = match src with @@ -233,6 +252,13 @@ module internal Memory = interface IStatedSymbolicConstantSource with override x.SubTerms = Seq.empty override x.Time = VectorTime.zero + override x.IndependentWith otherSource = + match otherSource with + | :? typeInitialized as otherType -> + let xDotNetType = toDotNetType x.typ + let otherDotNetType = toDotNetType otherType.typ + structuralInfimum xDotNetType otherDotNetType = None + | _ -> true let (|TypeInitializedSource|_|) (src : IStatedSymbolicConstantSource) = match src with diff --git a/VSharp.SILI.Core/Pointers.fs b/VSharp.SILI.Core/Pointers.fs index 17d4abbaa..527eeb22e 100644 --- a/VSharp.SILI.Core/Pointers.fs +++ b/VSharp.SILI.Core/Pointers.fs @@ -13,7 +13,8 @@ module internal Pointers = type private SymbolicPointerDifference(pos: list, neg: list) = interface ISymbolicConstantSource with override x.SubTerms = Seq.empty - override x.Time = VectorTime.zero + override x.Time = VectorTime.zero + override x.IndependentWith _ = false member this.Pos = pos member this.Neg = neg diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index 96d09eaeb..fb237dceb 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -219,6 +219,7 @@ and ISymbolicConstantSource = abstract SubTerms : term seq abstract Time : vectorTime + abstract IndependentWith : ISymbolicConstantSource -> bool type INonComposableSymbolicConstantSource = inherit ISymbolicConstantSource diff --git a/VSharp.SILI.Core/TypeCasting.fs b/VSharp.SILI.Core/TypeCasting.fs index 865ff5068..11428e33b 100644 --- a/VSharp.SILI.Core/TypeCasting.fs +++ b/VSharp.SILI.Core/TypeCasting.fs @@ -24,6 +24,10 @@ module internal TypeCasting = interface IStatedSymbolicConstantSource with override x.SubTerms = optCons (optCons [] x.left.SubTerm) x.right.SubTerm :> term seq override x.Time = VectorTime.zero + override x.IndependentWith otherSource = + match otherSource with + | :? symbolicSubtypeSource as otherSubtype -> x <> otherSubtype + | _ -> true let private makeSubtypeBoolConst left right = let subtypeName = sprintf "(%O <: %O)" left right From e788dd265f6b97baea7bf6154ae1ca71ca48b776 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 4 Oct 2021 18:11:45 +0300 Subject: [PATCH 06/88] Add conditions grouping by source independence --- VSharp.SILI.Core/Memory.fs | 2 +- VSharp.SILI.Core/PathConditionIndependent.fs | 39 ++++++++++++++------ VSharp.SILI.Core/Terms.fs | 20 ++-------- VSharp.Utils/PersistentUnionFind.fs | 2 + 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 30623cb2a..ccaffdc86 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -167,7 +167,7 @@ module internal Memory = match otherSource with | :? heapReading<'key, 'reg> as otherReading -> let rootRegions hr = match hr.memoryObject.updates with | Node dict -> PersistentDict.keys dict - rootRegions x |> Seq.forall (fun reg1 -> Seq.forall (fun reg2 -> reg1.CompareTo reg2 = Disjoint) (rootRegions otherReading)) + Seq.allPairs (rootRegions x) (rootRegions otherReading) |> Seq.forall (fun (reg1, reg2) -> reg1.CompareTo reg2 = Disjoint) | _ -> true let (|HeapReading|_|) (src : IMemoryAccessConstantSource) = diff --git a/VSharp.SILI.Core/PathConditionIndependent.fs b/VSharp.SILI.Core/PathConditionIndependent.fs index d38625a09..e8ba187c5 100644 --- a/VSharp.SILI.Core/PathConditionIndependent.fs +++ b/VSharp.SILI.Core/PathConditionIndependent.fs @@ -37,23 +37,35 @@ module internal PCI = | _ when isFalse pc -> falsePC | _ when PersistentDict.contains !!cond pc.constraintsWithConstants -> falsePC | _ -> - let consts = discoverConstantsRec cond - let tryHead = PersistentSet.toSeq >> Seq.tryHead + let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq + let someSetElement = PersistentSet.toSeq >> Seq.tryHead let pufWithNewConsts = - consts + condConsts |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind pc.constants t) |> PersistentSet.fold PersistentUnionFind.add pc.constants - let pufWithMergedConsts = - consts - |> tryHead + let constsWithSources = + Seq.map + (function + | ConstantT(_, src, _) as constant -> constant, src + | _ -> __unreachable__() + ) + let pufMergedByConstantSource = + Seq.allPairs + (condConsts |> PersistentSet.toSeq |> constsWithSources) + (pc.constants |> PersistentUnionFind.toSeq |> constsWithSources) + |> Seq.filter (fun ((_, src1), (_, src2)) -> not <| src1.IndependentWith src2) + |> Seq.fold (fun puf ((const1, _), (const2, _)) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts + let pufMergedByDependentCondition = + condConsts + |> someSetElement |> function | Some(parent) -> - PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufWithNewConsts consts - | None -> pufWithNewConsts - {constants = pufWithMergedConsts + PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts + | None -> pufMergedByConstantSource + {constants = pufMergedByDependentCondition constraintsWithConstants = - consts - |> tryHead + condConsts + |> someSetElement |> (fun head -> PersistentDict.add cond head pc.constraintsWithConstants)} let public mapPC mapper (pc : pathConditionIndependent) : pathConditionIndependent = @@ -65,7 +77,7 @@ module internal PCI = let toString pc = mapSeq toString pc |> Seq.sort |> join " /\ " - let union (pc1 : pathConditionIndependent) (pc2 : pathConditionIndependent) = Seq.fold add pc1 (toSeq pc2) + let public union (pc1 : pathConditionIndependent) (pc2 : pathConditionIndependent) = Seq.fold add pc1 (toSeq pc2) let public slice pc cond : pathCondition = PersistentDict.find pc.constraintsWithConstants cond @@ -86,4 +98,7 @@ module internal PCI = | _ -> __unreachable__()) |> PersistentSet.ofSeq | None -> PC.empty + + //let public fragments pc = + \ No newline at end of file diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index fb237dceb..f8e9fa145 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -496,6 +496,10 @@ module internal Terms = let (|UnionT|_|) = term >> function | Union gvs -> Some(UnionT gvs) | _ -> None + + let (|ConstantT|_|) = term >> function + | Constant(name, src, typ) -> Some(ConstantT name, src, typ) + | _ -> None let (|GuardedValues|_|) = function // TODO: this could be ineffective (because of unzip) | Union gvs -> Some(GuardedValues(List.unzip gvs)) @@ -643,22 +647,6 @@ module internal Terms = | _ -> () Seq.iter (iter addConstant) terms result :> ISet - - let discoverConstantsRec term = - let rec discoverConstantsInner acc term k = - match term.term with - | Nop | Concrete _ | HeapRef _ | Ref _ | Ptr(_, _, None) -> k acc - | Constant _ -> - PersistentSet.add acc term |> k - | Expression(_, operands, _) -> - Cps.List.foldlk discoverConstantsInner acc operands k - | Struct(fields, _) -> - Cps.Seq.foldlk discoverConstantsInner acc (PersistentDict.values fields) k - | Ptr(_, _, Some(term)) -> discoverConstantsInner acc term k - | Union(guardedTerms) -> - let guards, terms = List.unzip guardedTerms - Cps.Seq.foldlk discoverConstantsInner acc (List.append guards terms) k - discoverConstantsInner PersistentSet.empty term id let private foldFields isStatic folder acc typ = let dotNetType = Types.toDotNetType typ diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index 701a1f9c2..a6e54c75f 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -9,6 +9,8 @@ module public PersistentUnionFind = let public empty<'a when 'a : equality> : pUnionFind<'a> = {impl = PersistentDict.empty} + let public toSeq puf = PersistentDict.keys puf.impl + let rec public tryFind puf a = let tryFindInternal pdict a = try From f0a6581f4474c03c5e39eff1414571ff4f520af3 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 4 Oct 2021 21:10:37 +0300 Subject: [PATCH 07/88] Move independence logic to pathCondition from pathConditionIndependent --- VSharp.SILI.Core/PathCondition.fs | 69 ++++++++++-- VSharp.SILI.Core/PathConditionIndependent.fs | 104 ------------------- VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 1 - 3 files changed, 58 insertions(+), 116 deletions(-) delete mode 100644 VSharp.SILI.Core/PathConditionIndependent.fs diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 31bdf8784..e5eb70695 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -1,7 +1,10 @@ namespace VSharp.Core open VSharp +open VSharp.Utils +open VSharp.Utils.PersistentUnionFind -type pathCondition = pset +type pathCondition = + private {constants : pUnionFind; constraintsWithConstants : pdict} // Invariants: // - PC does not contain True @@ -9,25 +12,61 @@ type pathCondition = pset module internal PC = - let public empty = PersistentSet.empty - let public isEmpty pc = PersistentSet.isEmpty pc + let public empty = + {constants = PersistentUnionFind.empty + constraintsWithConstants = PersistentDict.empty} + + let public isEmpty pc = PersistentDict.isEmpty pc.constraintsWithConstants - let public toSeq pc = PersistentSet.toSeq pc + let public toSeq pc = PersistentDict.keys pc.constraintsWithConstants - let private falsePC = PersistentSet.add empty False + let private falsePC = + {constants = PersistentUnionFind.empty + constraintsWithConstants = PersistentDict.add False None PersistentDict.empty} + let public isFalse pc = - let isFalsePC = PersistentSet.contains False pc + let isFalsePC = PersistentDict.contains False pc.constraintsWithConstants if isFalsePC then assert(toSeq pc |> Seq.length = 1) isFalsePC - + let public add pc cond : pathCondition = match cond with | True -> pc | False -> falsePC | _ when isFalse pc -> falsePC - | _ when PersistentSet.contains !!cond pc -> falsePC - | _ -> PersistentSet.add pc cond - + | _ when PersistentDict.contains !!cond pc.constraintsWithConstants -> falsePC + | _ -> + let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq + let someSetElement = PersistentSet.toSeq >> Seq.tryHead + let pufWithNewConsts = + condConsts + |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind pc.constants t) + |> PersistentSet.fold PersistentUnionFind.add pc.constants + let constsWithSources = + Seq.map + (function + | ConstantT(_, src, _) as constant -> constant, src + | _ -> __unreachable__() + ) + let pufMergedByConstantSource = + Seq.allPairs + (condConsts |> PersistentSet.toSeq |> constsWithSources) + (pc.constants |> PersistentUnionFind.toSeq |> constsWithSources) + |> Seq.filter (fun ((_, src1), (_, src2)) -> not <| src1.IndependentWith src2) + |> Seq.fold (fun puf ((const1, _), (const2, _)) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts + let pufMergedByDependentCondition = + condConsts + |> someSetElement + |> function + | Some(parent) -> + PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts + | None -> pufMergedByConstantSource + {constants = pufMergedByDependentCondition + constraintsWithConstants = + condConsts + |> someSetElement + |> (fun head -> PersistentDict.add cond head pc.constraintsWithConstants)} + let public mapPC mapper (pc : pathCondition) : pathCondition = let mapAndAdd acc cond k = let acc' = mapper cond |> add acc @@ -37,4 +76,12 @@ module internal PC = let toString pc = mapSeq toString pc |> Seq.sort |> join " /\ " - let union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) + let public union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) + +(* let public fragments pc = + PersistentDict.fold (fun groups cond constant -> + PersistentUnionFind.tryFind pc.constants constant + |> PersistentDict.tryFind groups + |> function + | Some(ls) + )*) diff --git a/VSharp.SILI.Core/PathConditionIndependent.fs b/VSharp.SILI.Core/PathConditionIndependent.fs deleted file mode 100644 index e8ba187c5..000000000 --- a/VSharp.SILI.Core/PathConditionIndependent.fs +++ /dev/null @@ -1,104 +0,0 @@ -module VSharp.SILI.Core.PathConditionIndependent - -open VSharp -open VSharp.Core -open VSharp.Utils.PersistentUnionFind - -type pathConditionIndependent = - private {constants : pUnionFind; constraintsWithConstants : pdict} - -// Invariants: -// - PCI does not contain True -// - if PCI contains False then False is the only element in PCI - -module internal PCI = - - let public empty = - {constants = PersistentUnionFind.empty - constraintsWithConstants = PersistentDict.empty} - - let public isEmpty pc = PersistentDict.isEmpty pc.constraintsWithConstants - - let public toSeq pc = PersistentDict.keys pc.constraintsWithConstants - - let private falsePC = - {constants = PersistentUnionFind.empty - constraintsWithConstants = PersistentDict.add False None PersistentDict.empty} - - let public isFalse pc = - let isFalsePC = PersistentDict.contains False pc.constraintsWithConstants - if isFalsePC then assert(toSeq pc |> Seq.length = 1) - isFalsePC - - let public add pc cond : pathConditionIndependent = - match cond with - | True -> pc - | False -> falsePC - | _ when isFalse pc -> falsePC - | _ when PersistentDict.contains !!cond pc.constraintsWithConstants -> falsePC - | _ -> - let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq - let someSetElement = PersistentSet.toSeq >> Seq.tryHead - let pufWithNewConsts = - condConsts - |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind pc.constants t) - |> PersistentSet.fold PersistentUnionFind.add pc.constants - let constsWithSources = - Seq.map - (function - | ConstantT(_, src, _) as constant -> constant, src - | _ -> __unreachable__() - ) - let pufMergedByConstantSource = - Seq.allPairs - (condConsts |> PersistentSet.toSeq |> constsWithSources) - (pc.constants |> PersistentUnionFind.toSeq |> constsWithSources) - |> Seq.filter (fun ((_, src1), (_, src2)) -> not <| src1.IndependentWith src2) - |> Seq.fold (fun puf ((const1, _), (const2, _)) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts - let pufMergedByDependentCondition = - condConsts - |> someSetElement - |> function - | Some(parent) -> - PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts - | None -> pufMergedByConstantSource - {constants = pufMergedByDependentCondition - constraintsWithConstants = - condConsts - |> someSetElement - |> (fun head -> PersistentDict.add cond head pc.constraintsWithConstants)} - - let public mapPC mapper (pc : pathConditionIndependent) : pathConditionIndependent = - let mapAndAdd acc cond k = - let acc' = mapper cond |> add acc - if isFalse acc' then falsePC else k acc' - Cps.Seq.foldlk mapAndAdd empty (toSeq pc) id - let public mapSeq mapper (pc : pathConditionIndependent) = toSeq pc |> Seq.map mapper - - let toString pc = mapSeq toString pc |> Seq.sort |> join " /\ " - - let public union (pc1 : pathConditionIndependent) (pc2 : pathConditionIndependent) = Seq.fold add pc1 (toSeq pc2) - - let public slice pc cond : pathCondition = - PersistentDict.find pc.constraintsWithConstants cond - |> function - | Some(constant) -> PersistentUnionFind.tryFind pc.constants constant - | None -> None - |> function - | Some _ as parent -> - pc.constraintsWithConstants - |> PersistentDict.toSeq - |> Seq.filter - (function - | _, Some c -> parent = PersistentUnionFind.tryFind pc.constants c - | _ -> false) - |> Seq.map - (function - | t, Some _ -> t - | _ -> __unreachable__()) - |> PersistentSet.ofSeq - | None -> PC.empty - - //let public fragments pc = - - \ No newline at end of file diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index a3078e04e..d42f5f2b1 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -39,7 +39,6 @@ - From e11557422c57db24774eae21c32545f56092fb9e Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Thu, 7 Oct 2021 22:34:29 +0300 Subject: [PATCH 08/88] Rename constraintsWithConstants field --- VSharp.SILI.Core/PathCondition.fs | 47 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index e5eb70695..14a82d2d8 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -4,7 +4,7 @@ open VSharp.Utils open VSharp.Utils.PersistentUnionFind type pathCondition = - private {constants : pUnionFind; constraintsWithConstants : pdict} + private {constants : pUnionFind; conditionsWithConstants : pdict} // Invariants: // - PC does not contain True @@ -14,30 +14,31 @@ module internal PC = let public empty = {constants = PersistentUnionFind.empty - constraintsWithConstants = PersistentDict.empty} + conditionsWithConstants = PersistentDict.empty} - let public isEmpty pc = PersistentDict.isEmpty pc.constraintsWithConstants + let public isEmpty pc = PersistentDict.isEmpty pc.conditionsWithConstants - let public toSeq pc = PersistentDict.keys pc.constraintsWithConstants + let public toSeq pc = PersistentDict.keys pc.conditionsWithConstants let private falsePC = {constants = PersistentUnionFind.empty - constraintsWithConstants = PersistentDict.add False None PersistentDict.empty} - + conditionsWithConstants = PersistentDict.add False None PersistentDict.empty} + let public isFalse pc = - let isFalsePC = PersistentDict.contains False pc.constraintsWithConstants + let isFalsePC = PersistentDict.contains False pc.conditionsWithConstants if isFalsePC then assert(toSeq pc |> Seq.length = 1) isFalsePC + let private someSetElement = PersistentSet.toSeq >> Seq.tryHead + let public add pc cond : pathCondition = match cond with | True -> pc | False -> falsePC | _ when isFalse pc -> falsePC - | _ when PersistentDict.contains !!cond pc.constraintsWithConstants -> falsePC + | _ when PersistentDict.contains !!cond pc.conditionsWithConstants -> falsePC | _ -> let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq - let someSetElement = PersistentSet.toSeq >> Seq.tryHead let pufWithNewConsts = condConsts |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind pc.constants t) @@ -62,10 +63,10 @@ module internal PC = PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts | None -> pufMergedByConstantSource {constants = pufMergedByDependentCondition - constraintsWithConstants = + conditionsWithConstants = condConsts |> someSetElement - |> (fun head -> PersistentDict.add cond head pc.constraintsWithConstants)} + |> (fun head -> PersistentDict.add cond head pc.conditionsWithConstants)} let public mapPC mapper (pc : pathCondition) : pathCondition = let mapAndAdd acc cond k = @@ -79,9 +80,21 @@ module internal PC = let public union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) (* let public fragments pc = - PersistentDict.fold (fun groups cond constant -> - PersistentUnionFind.tryFind pc.constants constant - |> PersistentDict.tryFind groups - |> function - | Some(ls) - )*) + let groups = PersistentDict.fold (fun groups cond constants -> + let parent = + constants + |> someSetElement + |> function + | Some(someConst) -> PersistentUnionFind.tryFind pc.constants someConst + | None -> None + let updatedGroup = + PersistentDict.tryFind groups parent + |> function + | Some(condList) -> cond :: condList + | None -> [cond] + PersistentDict.add parent updatedGroup groups + ) PersistentDict.empty pc.conditionsWithConstants + groups + |> PersistentDict.map (fun _ conds -> + let + )*) \ No newline at end of file From 3dcfc0b23323e5af46487f70df5744eda7ae42a2 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 8 Oct 2021 12:48:32 +0300 Subject: [PATCH 09/88] Make persistent union-find cyclic --- VSharp.SILI.Core/PathCondition.fs | 2 +- VSharp.Utils/PersistentUnionFind.fs | 56 ++++++++++++++++++----------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 14a82d2d8..57e813915 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -41,7 +41,7 @@ module internal PC = let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq let pufWithNewConsts = condConsts - |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind pc.constants t) + |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind t pc.constants) |> PersistentSet.fold PersistentUnionFind.add pc.constants let constsWithSources = Seq.map diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index a6e54c75f..ac0c0b684 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -2,35 +2,49 @@ open VSharp -type public pUnionFind<'a> when 'a : equality = - private {impl : pdict<'a, 'a>} +type private node<'a> = + | Tail of 'a + | Node of 'a + +type public pUnionFind<'a> when 'a : equality = + private {elements : pdict<'a, node<'a>>} module public PersistentUnionFind = - let public empty<'a when 'a : equality> : pUnionFind<'a> = {impl = PersistentDict.empty} + let public empty<'a when 'a : equality> : pUnionFind<'a> = + {elements = PersistentDict.empty} - let public toSeq puf = PersistentDict.keys puf.impl + let public toSeq puf = PersistentDict.keys puf.elements - let rec public tryFind puf a = - let tryFindInternal pdict a = - try - PersistentDict.find pdict a |> Some - with - _ -> None - match tryFindInternal puf.impl a with - | Some b when a = b -> Some a - | Some b -> tryFind puf b - | None -> None + let rec public find a puf = + PersistentDict.find puf.elements a + |> function + | Tail _ -> a + | Node(next) -> find next puf + + let public tryFind a puf = + try + find a puf |> Some + with + _ -> None let public union a b puf = - let aParentOption = tryFind puf a - let bParentOption = tryFind puf b + let aParentOption = tryFind a puf + let bParentOption = tryFind b puf match aParentOption, bParentOption with - | Some aParent, Some bParent when aParent <> bParent -> - {impl = PersistentDict.add aParent bParent puf.impl} - | _ -> puf + | Some(aParent), Some(bParent) when aParent <> bParent -> + let unwrap = function + | Tail value -> value + | Node value -> value + let aTail = PersistentDict.find puf.elements aParent |> unwrap + let bTail = PersistentDict.find puf.elements bParent |> unwrap + puf.elements + |> PersistentDict.add aParent (Tail(bTail)) + |> PersistentDict.add bParent (Node(aTail)) + |> (fun pdict -> {elements = pdict}) + | _ -> puf let public add puf a = - match tryFind puf a with + match tryFind a puf with | Some _ -> puf - | None -> {impl = PersistentDict.add a a puf.impl} \ No newline at end of file + | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} \ No newline at end of file From d2d6ceed7c03e67e0cdd0d4a5062925c08700e83 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 8 Oct 2021 13:59:51 +0300 Subject: [PATCH 10/88] Add subset function to persistent union find --- VSharp.Utils/PersistentUnionFind.fs | 36 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index ac0c0b684..4dfffe2d8 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -1,6 +1,7 @@ module VSharp.Utils.PersistentUnionFind open VSharp +open VSharp.Utils type private node<'a> = | Tail of 'a @@ -28,23 +29,36 @@ module public PersistentUnionFind = with _ -> None + let private unwrapNode = function + | Tail value -> value + | Node value -> value + let public union a b puf = let aParentOption = tryFind a puf let bParentOption = tryFind b puf match aParentOption, bParentOption with | Some(aParent), Some(bParent) when aParent <> bParent -> - let unwrap = function - | Tail value -> value - | Node value -> value - let aTail = PersistentDict.find puf.elements aParent |> unwrap - let bTail = PersistentDict.find puf.elements bParent |> unwrap - puf.elements - |> PersistentDict.add aParent (Tail(bTail)) - |> PersistentDict.add bParent (Node(aTail)) - |> (fun pdict -> {elements = pdict}) + let aTail = PersistentDict.find puf.elements aParent |> unwrapNode + let bTail = PersistentDict.find puf.elements bParent |> unwrapNode + let mergedElements = + puf.elements + |> PersistentDict.add aParent (Tail(bTail)) + |> PersistentDict.add bParent (Node(aTail)) + {elements = mergedElements} | _ -> puf - let public add puf a = + let public add a puf = match tryFind a puf with | Some _ -> puf - | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} \ No newline at end of file + | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} + + let public subset a puf = + let rec traverse current acc = + let next = PersistentDict.find puf.elements current + let updatedDict = PersistentDict.add current next acc + let unwrappedNext = unwrapNode next + if (unwrappedNext <> a) then + traverse unwrappedNext updatedDict + else updatedDict + let subsetElements = traverse a PersistentDict.empty + {elements = subsetElements} \ No newline at end of file From b0f02e961ae7a7a3def8b3cdaab7f284d6fe6da0 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 8 Oct 2021 14:53:54 +0300 Subject: [PATCH 11/88] Add fragments iterator to pathCondition --- VSharp.SILI.Core/PathCondition.fs | 46 +++++++++++++++++------------ VSharp.Utils/PersistentUnionFind.fs | 3 +- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 57e813915..ba837af41 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -79,22 +79,30 @@ module internal PC = let public union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) -(* let public fragments pc = - let groups = PersistentDict.fold (fun groups cond constants -> - let parent = - constants - |> someSetElement - |> function - | Some(someConst) -> PersistentUnionFind.tryFind pc.constants someConst - | None -> None - let updatedGroup = - PersistentDict.tryFind groups parent - |> function - | Some(condList) -> cond :: condList - | None -> [cond] - PersistentDict.add parent updatedGroup groups - ) PersistentDict.empty pc.conditionsWithConstants - groups - |> PersistentDict.map (fun _ conds -> - let - )*) \ No newline at end of file + let public fragments pc = + pc.conditionsWithConstants + |> PersistentDict.fold + (fun groups cond constant -> + let parent = + constant + |> function + | Some(someConst) -> PersistentUnionFind.tryFind someConst pc.constants + | None -> None + let updatedGroup = + PersistentDict.tryFind groups parent + |> function + | Some(condSet) -> PersistentSet.add condSet cond + | None -> PersistentSet.add PersistentSet.empty cond + PersistentDict.add parent updatedGroup groups + ) PersistentDict.empty + |> PersistentDict.toSeq + |> Seq.map + (fun (parent, conds) -> + let fragmentConstants = + match parent with + | Some(parent) -> PersistentUnionFind.subset parent pc.constants + | None -> PersistentUnionFind.empty + let fragmentConditionsWithConstants = + PersistentSet.fold (fun dict cond -> PersistentDict.add cond parent dict) PersistentDict.empty conds + {constants = fragmentConstants; conditionsWithConstants = fragmentConditionsWithConstants} + ) \ No newline at end of file diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index 4dfffe2d8..afb8eab55 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -1,7 +1,6 @@ module VSharp.Utils.PersistentUnionFind open VSharp -open VSharp.Utils type private node<'a> = | Tail of 'a @@ -47,7 +46,7 @@ module public PersistentUnionFind = {elements = mergedElements} | _ -> puf - let public add a puf = + let public add puf a = match tryFind a puf with | Some _ -> puf | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} From 147a3bc58b8b7c99dfab80a52412bab70f9e1d97 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 8 Oct 2021 16:10:21 +0300 Subject: [PATCH 12/88] Add some comments --- VSharp.SILI.Core/PathCondition.fs | 14 +++++++++++++ VSharp.Utils/PersistentUnionFind.fs | 31 ++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index ba837af41..f93e21c7b 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -3,6 +3,11 @@ open VSharp open VSharp.Utils open VSharp.Utils.PersistentUnionFind +(* + Path condition is represented as a union-find of sets disjoint by independence of + constants in them and a dictionary where a condition is mapped to some constant + contained in it or None if the condition doesn't contain any constants +*) type pathCondition = private {constants : pUnionFind; conditionsWithConstants : pdict} @@ -49,12 +54,17 @@ module internal PC = | ConstantT(_, src, _) as constant -> constant, src | _ -> __unreachable__() ) + // Merge sets of constants dependent in terms of ISymbolicConstantSource let pufMergedByConstantSource = Seq.allPairs (condConsts |> PersistentSet.toSeq |> constsWithSources) (pc.constants |> PersistentUnionFind.toSeq |> constsWithSources) |> Seq.filter (fun ((_, src1), (_, src2)) -> not <| src1.IndependentWith src2) |> Seq.fold (fun puf ((const1, _), (const2, _)) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts + (* + Merge sets of constants dependent in terms of reachability in graph where + edge between constants means that they are contained in the same condition + *) let pufMergedByDependentCondition = condConsts |> someSetElement @@ -79,6 +89,10 @@ module internal PC = let public union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) + /// + /// Returns the sequence of path conditions such that constants contained in + /// one path condition are independent with constants contained in another one + /// let public fragments pc = pc.conditionsWithConstants |> PersistentDict.fold diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index afb8eab55..dd30c8ed0 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -2,10 +2,18 @@ open VSharp +(* + Union-find sets are implemented with dictionary cyclically mapping previous + value to the next value wrapped with node. The last element (i. e. element before Tail) + is considered as representative parent element of the set +*) type private node<'a> = | Tail of 'a | Node of 'a - + +/// +/// Persistent union-find (disjoint-set) structure +/// type public pUnionFind<'a> when 'a : equality = private {elements : pdict<'a, node<'a>>} @@ -16,12 +24,20 @@ module public PersistentUnionFind = let public toSeq puf = PersistentDict.keys puf.elements + /// + /// Returns representative element of the set containing the given element or + /// throws if the given element not found + /// let rec public find a puf = PersistentDict.find puf.elements a |> function | Tail _ -> a | Node(next) -> find next puf + /// + /// Returns representative element of the set containing the given element or + /// None if the given element not found + /// let public tryFind a puf = try find a puf |> Some @@ -31,7 +47,10 @@ module public PersistentUnionFind = let private unwrapNode = function | Tail value -> value | Node value -> value - + + /// + /// Unions two sets containing the given elements + /// let public union a b puf = let aParentOption = tryFind a puf let bParentOption = tryFind b puf @@ -45,12 +64,18 @@ module public PersistentUnionFind = |> PersistentDict.add bParent (Node(aTail)) {elements = mergedElements} | _ -> puf - + + /// + /// Adds a single-element set containing the given element + /// let public add puf a = match tryFind a puf with | Some _ -> puf | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} + /// + /// Returns a single-set union-find with the set containing the given element + /// let public subset a puf = let rec traverse current acc = let next = PersistentDict.find puf.elements current From e7839b4ec0b8bf7965d18bf31ec3eb1cc028778e Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sat, 16 Oct 2021 21:17:23 +0300 Subject: [PATCH 13/88] Add some persistent union find tests --- VSharp.SILI.Core/PathCondition.fs | 1 - VSharp.Test/PersistentUnionFindTests.cs | 141 ++++++++++++++++++++++++ VSharp.Utils/PersistentUnionFind.fs | 25 +++-- 3 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 VSharp.Test/PersistentUnionFindTests.cs diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index f93e21c7b..bba1955b4 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -1,7 +1,6 @@ namespace VSharp.Core open VSharp open VSharp.Utils -open VSharp.Utils.PersistentUnionFind (* Path condition is represented as a union-find of sets disjoint by independence of diff --git a/VSharp.Test/PersistentUnionFindTests.cs b/VSharp.Test/PersistentUnionFindTests.cs new file mode 100644 index 000000000..ddfb12ffe --- /dev/null +++ b/VSharp.Test/PersistentUnionFindTests.cs @@ -0,0 +1,141 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using static VSharp.PersistentUnionFind; + +namespace VSharp.Test +{ + [TestFixture] + public sealed class PersistentUnionFindTests + { + private const string foo = "foo"; + private const string bar = "bar"; + private const string baz = "baz"; + + private pUnionFind stringUnionFind; + private pUnionFind intUnionFind; + + [SetUp] + public void SetUp() + { + stringUnionFind = empty(); + intUnionFind = empty(); + } + + [Test] + public void SingleElementSetsParentsTest() + { + Add(ref stringUnionFind, foo); + Add(ref stringUnionFind, bar); + var fooParent = find(foo, stringUnionFind); + var barParent = find(bar, stringUnionFind); + Assert.AreEqual(foo, fooParent); + Assert.AreEqual(bar, barParent); + } + + [Test] + public void ElementsOfUnionHaveSameParentTest1() + { + Add(ref stringUnionFind, foo); + Add(ref stringUnionFind, bar); + Add(ref stringUnionFind, baz); + Union(foo, bar, ref stringUnionFind); + Union(foo, baz, ref stringUnionFind); + var fooParent = find(foo, stringUnionFind); + var barParent = find(bar, stringUnionFind); + var bazParent = find(baz, stringUnionFind); + Assert.AreEqual(fooParent, barParent); + Assert.AreEqual(barParent, bazParent); + } + + [Test] + public void ElementsOfUnionHaveSameParentTest2() + { + Add(ref intUnionFind, 1); + Add(ref intUnionFind, 2); + + for (var i = 3; i <= 100; ++i) + { + Add(ref intUnionFind, i); + Union(i, 2 - i % 2, ref intUnionFind); + } + + var parent1 = find(1, intUnionFind); + var parent2 = find(2, intUnionFind); + + for (var i = 1; i <= 100; ++i) + { + var actualParent = find(i, intUnionFind); + var expectedParent = i % 2 == 0 ? parent2 : parent1; + Assert.AreEqual(expectedParent, actualParent); + } + + Union(21, 54, ref intUnionFind); + + var unionParent = find(1, intUnionFind); + + for (var i = 1; i <= 100; ++i) + { + var actualParent = find(i, intUnionFind); + Assert.AreEqual(unionParent, actualParent); + } + } + + [Test] + public void SubsetTest() + { + Add(ref stringUnionFind, foo); + Add(ref stringUnionFind, bar); + Add(ref stringUnionFind, baz); + Union(foo, bar, ref stringUnionFind); + var fooBarSubset = new List(toSeq(subset(foo, stringUnionFind))); + var bazSubset = new List(toSeq(subset(baz, stringUnionFind))); + var expectedFooBarSubset = new List { foo, bar }; + var expectedBazSubset = new List { baz }; + Assert.That(fooBarSubset, Is.EquivalentTo(expectedFooBarSubset)); + Assert.That(bazSubset, Is.EquivalentTo(expectedBazSubset)); + } + + [Test] + public void FindForNonexistentElementThrowsTest() + { + Add(ref stringUnionFind, foo); + Assert.Throws(() => find(bar, stringUnionFind)); + } + + [Test] + public void TryFindTest() + { + Add(ref stringUnionFind, foo); + var found = tryFind(foo, stringUnionFind).Value; + Assert.AreEqual(foo, found); + } + + [Test] + public void TryFindForNonexistentElementReturnsNoneTest() + { + Add(ref stringUnionFind, foo); + Assert.Throws(() => _ = tryFind(bar, stringUnionFind).Value); + } + + [Test] + public void AddExistentElementDoesNothingTest() + { + Add(ref stringUnionFind, foo); + Add(ref stringUnionFind, foo); + var actualElements = new List(toSeq(stringUnionFind)); + var expectedElements = new List { foo }; + Assert.That(actualElements, Is.EquivalentTo(expectedElements)); + } + + private void Add (ref pUnionFind unionFind, T element) + { + unionFind = add(unionFind, element); + } + + private void Union (T one, T another, ref pUnionFind unionFind) + { + unionFind = union(one, another, unionFind); + } + } +} diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index dd30c8ed0..b9492b7d1 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -1,5 +1,6 @@ -module VSharp.Utils.PersistentUnionFind +namespace VSharp +open System open VSharp (* @@ -28,11 +29,15 @@ module public PersistentUnionFind = /// Returns representative element of the set containing the given element or /// throws if the given element not found /// + /// Element not found let rec public find a puf = - PersistentDict.find puf.elements a - |> function - | Tail _ -> a - | Node(next) -> find next puf + try + PersistentDict.find puf.elements a + |> function + | Tail _ -> a + | Node(next) -> find next puf + with + _ -> raise (InvalidOperationException "Element not found") /// /// Returns representative element of the set containing the given element or @@ -66,7 +71,8 @@ module public PersistentUnionFind = | _ -> puf /// - /// Adds a single-element set containing the given element + /// Adds a single-element set containing the given element. If the element already exists, + /// does nothing /// let public add puf a = match tryFind a puf with @@ -76,9 +82,14 @@ module public PersistentUnionFind = /// /// Returns a single-set union-find with the set containing the given element /// + /// Element not found let public subset a puf = let rec traverse current acc = - let next = PersistentDict.find puf.elements current + let next = + try + PersistentDict.find puf.elements current + with + _ -> raise (InvalidOperationException "Element not found") let updatedDict = PersistentDict.add current next acc let unwrappedNext = unwrapNode next if (unwrappedNext <> a) then From f91a799f2894f59994100517fee5709925fbb1a6 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Tue, 19 Oct 2021 14:16:05 +0300 Subject: [PATCH 14/88] Add with independent by function and some sandbox code --- VSharp.SILI.Core/Memory.fs | 10 +++++ .../Tests/ConstraintIndependenceSandbox.cs | 40 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 VSharp.Test/Tests/ConstraintIndependenceSandbox.cs diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index ccaffdc86..cbe72e9be 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -46,6 +46,16 @@ module internal Memory = x = VectorTime.zero let withPathCondition (s : state) cond : state = { s with pc = PC.add s.pc cond } + + let withIndependentBy (s : state) cond : state = + let fragment = + PC.fragments s.pc + |> Seq.tryFind (PC.toSeq >> Seq.contains cond) + |> function + | Some(fragment) -> fragment + | None -> raise InvalidOperationException "" + { s with pc = fragment } + // ------------------------------- Stack ------------------------------- diff --git a/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs b/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs new file mode 100644 index 000000000..2e0873e8b --- /dev/null +++ b/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs @@ -0,0 +1,40 @@ +namespace VSharp.Test.Tests +{ + [TestSvmFixture] + public class ConstraintIndependenceSandbox + { + [TestSvm] + public static int Sandbox(int a, int b) + { + if (a != b) + { + if (a > 100) + { + if (b < 200) + { + return 1; + } + else + { + return 2; + } + } + else + { + if (b < 150) + { + return 3; + } + else + { + return 4; + } + } + } + else + { + return 5; + } + } + } +} \ No newline at end of file From 22b44c13477ac6f8d15505a63a8e0fac55e1c1f9 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 20 Oct 2021 22:11:58 +0300 Subject: [PATCH 15/88] Update commonStatedConditionalExecutionk to use constraint independence --- VSharp.SILI.Core/Memory.fs | 44 ++++++++++++------- .../Tests/ConstraintIndependenceSandbox.cs | 6 +-- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index b86c383c2..d65ddf643 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -80,15 +80,6 @@ module internal Memory = add acc absOffset List.fold attachOne (makeNumber 0) [0 .. length - 1] - let withIndependentBy (s : state) cond : state = - let fragment = - PC.fragments s.pc - |> Seq.tryFind (PC.toSeq >> Seq.contains cond) - |> function - | Some(fragment) -> fragment - | None -> raise InvalidOperationException "" - { s with pc = fragment } - // ------------------------------- Stack ------------------------------- let newStackFrame (s : state) m frame = @@ -185,7 +176,15 @@ module internal Memory = {object : term} interface IStatedSymbolicConstantSource with override x.SubTerms = Seq.empty - override x.Time = VectorTime.zero + override x.Time = VectorTime.zero + override x.IndependentWith otherSource = + match otherSource with + | :? hashCodeSource as otherHashCodeSource -> + match otherHashCodeSource.object, x.object with + | ConstantT(_, otherConstantSource, _), ConstantT(_, constantSource, _) -> + otherConstantSource.IndependentWith constantSource + | _ -> true + | _ -> true let hashConcreteAddress (address : concreteHeapAddress) = address.GetHashCode() |> makeNumber @@ -680,37 +679,47 @@ module internal Memory = commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let keepIndependentWith pc cond = + PC.fragments pc + |> Seq.tryFind (PC.toSeq >> Seq.contains cond) + |> function + | Some(fragment) -> fragment + | None -> pc let execution thenState elseState condition k = assert (condition <> True && condition <> False) thenBranch thenState (fun thenResult -> elseBranch elseState (fun elseResult -> merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> + conditionInvocation state (fun (condition, conditionState) -> + let negatedCondition = !!condition let thenPc = PC.add state.pc condition - let elsePc = PC.add state.pc (!!condition) - if PC.isFalse thenPc then + let elsePc = PC.add state.pc negatedCondition + let independentThenPc = keepIndependentWith thenPc condition + let independentElsePc = keepIndependentWith elsePc negatedCondition + if PC.isFalse independentThenPc then conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) - elif PC.isFalse elsePc then + elif PC.isFalse independentElsePc then conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - conditionState.pc <- thenPc + conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc + conditionState.pc <- independentElsePc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> __insufficientInformation__ "Unable to witness branch" | SolverInteraction.SmtSat model -> + conditionState.pc <- thenPc conditionState.model <- Some model.mdl elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat model -> - conditionState.pc <- elsePc + conditionState.pc <- independentElsePc conditionState.model <- Some model.mdl match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ @@ -718,6 +727,7 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat model -> + conditionState.pc <- elsePc let thenState = conditionState let elseState = copy conditionState elsePc elseState.model <- Some model.mdl diff --git a/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs b/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs index 2e0873e8b..7655097b8 100644 --- a/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs +++ b/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs @@ -4,13 +4,13 @@ public class ConstraintIndependenceSandbox { [TestSvm] - public static int Sandbox(int a, int b) + public static int Sandbox(int a, int b, int c) { - if (a != b) + if (c > 0) { if (a > 100) { - if (b < 200) + if (b == c) { return 1; } From 2ca9dbdfd246602d8e273be264b7fb785a496625 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sun, 14 Nov 2021 18:37:11 +0300 Subject: [PATCH 16/88] Update model in Z3 module instead of creating it --- VSharp.SILI.Core/API.fs | 7 ++-- VSharp.SILI.Core/API.fsi | 3 ++ VSharp.SILI.Core/Database.fs | 2 +- VSharp.SILI.Core/Memory.fs | 30 +++-------------- VSharp.SILI.Core/SolverInteraction.fs | 9 ++++- VSharp.SILI.Core/State.fs | 25 ++++++++++++++ VSharp.Solver/Z3.fs | 48 +++++++++++++++------------ 7 files changed, 72 insertions(+), 52 deletions(-) diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 0bf12b8d0..8e07445e1 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -237,7 +237,7 @@ module API = let EmptyStack = EvaluationStack.empty module public Memory = - let EmptyState() = Memory.makeEmpty() + let EmptyState() = State.makeEmpty() let PopFrame state = Memory.popFrame state let ForcePopFrames count state = Memory.forcePopFrames count state let PopTypeVariables state = Memory.popTypeVariablesSubstitution state @@ -352,6 +352,8 @@ module API = let InitializeStaticMembers state targetType = Memory.initializeStaticMembers state targetType + + let Allocate state key term = Memory.allocateOnStack state key term let AllocateTemporaryLocalVariable state typ term = let tmpKey = TemporaryLocalVariableKey typ @@ -489,7 +491,8 @@ module API = state.lowerBounds <- PersistentDict.update state.lowerBounds typ (MemoryRegion.empty Types.lengthType) (MemoryRegion.fillRegion value) | StackBufferSort key -> state.stackBuffers <- PersistentDict.update state.stackBuffers key (MemoryRegion.empty Types.Int8) (MemoryRegion.fillRegion value) - + + let IsStackEmpty state = CallStack.isEmpty state.stack module Print = let Dump state = Memory.dump state diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index e80941de1..82816261e 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -240,6 +240,7 @@ module API = val InitializeStaticMembers : state -> symbolicType -> unit + val Allocate : state -> stackKey -> term -> unit val AllocateTemporaryLocalVariable : state -> System.Type -> term -> term val AllocateDefaultClass : state -> symbolicType -> term val AllocateDefaultArray : state -> term list -> symbolicType -> term @@ -279,6 +280,8 @@ module API = val FillRegion : state -> term -> regionSort -> unit + + val IsStackEmpty : state -> bool module Print = val Dump : state -> string diff --git a/VSharp.SILI.Core/Database.fs b/VSharp.SILI.Core/Database.fs index 59a3dfec6..cf0a3cfd6 100644 --- a/VSharp.SILI.Core/Database.fs +++ b/VSharp.SILI.Core/Database.fs @@ -186,7 +186,7 @@ type path = sprintf "{path [lvl %s]: %O}" (Level.toString x.lvl) x.state.pc type query = - { lvl : level; queryFml : formula } with + { lvl : level; queryFml : formula; currentModel : model } with override x.ToString() = sprintf "{query [lvl %s]: %O}" (Level.toString x.lvl) x.queryFml diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index d65ddf643..aa2b90add 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -18,30 +18,7 @@ type IMemoryAccessConstantSource = module internal Memory = // ------------------------------- Primitives ------------------------------- - - let makeEmpty() = { - pc = PC.empty - evaluationStack = EvaluationStack.empty - exceptionsRegister = NoException - stack = CallStack.empty - stackBuffers = PersistentDict.empty - classFields = PersistentDict.empty - arrays = PersistentDict.empty - lengths = PersistentDict.empty - lowerBounds = PersistentDict.empty - staticFields = PersistentDict.empty - boxedLocations = PersistentDict.empty - initializedTypes = SymbolicSet.empty - concreteMemory = Dictionary<_,_>() - physToVirt = PersistentDict.empty - allocatedTypes = PersistentDict.empty - typeVariables = (MappedStack.empty, Stack.empty) - delegates = PersistentDict.empty - currentTime = [1] - startingTime = VectorTime.zero - model = None - } - + type memoryMode = | ConcreteMemory | SymbolicMemory @@ -695,6 +672,7 @@ module internal Memory = let thenPc = PC.add state.pc condition let elsePc = PC.add state.pc negatedCondition let independentThenPc = keepIndependentWith thenPc condition + // Unnecessary let independentElsePc = keepIndependentWith elsePc negatedCondition if PC.isFalse independentThenPc then conditionState.pc <- elsePc @@ -726,11 +704,11 @@ module internal Memory = | SolverInteraction.SmtUnknown _ -> conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model -> + | SolverInteraction.SmtSat model2 -> conditionState.pc <- elsePc let thenState = conditionState let elseState = copy conditionState elsePc - elseState.model <- Some model.mdl + elseState.model <- Some model2.mdl thenState.pc <- thenPc execution thenState elseState condition k) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index d0c32cb77..b92924631 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -1,5 +1,7 @@ namespace VSharp.Core +open System +open System.Collections.Generic open FSharpx.Collections open VSharp @@ -38,5 +40,10 @@ module public SolverInteraction = let ctx = getEncodingContext state let formula = PC.toSeq state.pc |> conjunction match solver with - | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } + | Some s -> + let model = + match state.model with + | Some(m) -> m + | None -> { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } + s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } | None -> SmtUnknown "" diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 20828d4ce..5a6597951 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -141,3 +141,28 @@ and IStatedSymbolicConstantSource = inherit ISymbolicConstantSource abstract Compose : state -> term + +module public State = + + let makeEmpty() = { + pc = PC.empty + evaluationStack = EvaluationStack.empty + exceptionsRegister = NoException + stack = CallStack.empty + stackBuffers = PersistentDict.empty + classFields = PersistentDict.empty + arrays = PersistentDict.empty + lengths = PersistentDict.empty + lowerBounds = PersistentDict.empty + staticFields = PersistentDict.empty + boxedLocations = PersistentDict.empty + initializedTypes = SymbolicSet.empty + concreteMemory = Dictionary<_,_>() + physToVirt = PersistentDict.empty + allocatedTypes = PersistentDict.empty + typeVariables = (MappedStack.empty, Stack.empty) + delegates = PersistentDict.empty + currentTime = [1] + startingTime = VectorTime.zero + model = None + } diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index f1386f095..9c734e468 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -639,43 +639,43 @@ module internal Z3 = structureRef := x.WriteFields !structureRef value fields - member x.MkModel (m : Model) = - let subst = Dictionary() + member x.UpdateModel (z3Model : Model) (targetModel : model) = let stackEntries = Dictionary() encodingCache.t2e |> Seq.iter (fun kvp -> match kvp.Key with | {term = Constant(_, StructFieldChain(fields, StackReading(key)), t)} -> - let refinedExpr = m.Eval(kvp.Value.expr, false) + let refinedExpr = z3Model.Eval(kvp.Value.expr, false) x.Decode t refinedExpr |> x.WriteDictOfValueTypes stackEntries key fields key.TypeOfLocation | {term = Constant(_, (:? IMemoryAccessConstantSource as ms), _)} -> match ms with | HeapAddressSource(StackReading(key)) -> - let refinedExpr = m.Eval(kvp.Value.expr, false) + let refinedExpr = z3Model.Eval(kvp.Value.expr, false) let t = key.TypeOfLocation let addr = refinedExpr |> x.DecodeConcreteHeapAddress t |> ConcreteHeapAddress stackEntries.Add(key, HeapRef addr t |> ref) | _ -> () | {term = Constant(_, :? IStatedSymbolicConstantSource, _)} -> () | {term = Constant(_, source, t)} -> - let refinedExpr = m.Eval(kvp.Value.expr, false) + let refinedExpr = z3Model.Eval(kvp.Value.expr, false) let term = x.Decode t refinedExpr - subst.Add(source, term) + targetModel.subst.[source] <- term | _ -> ()) - let state = Memory.EmptyState() - let frame = stackEntries |> Seq.map (fun kvp -> + if (Memory.IsStackEmpty targetModel.state) then + Memory.NewStackFrame targetModel.state null List.empty + + // Check that stack is not empty + stackEntries |> Seq.iter (fun kvp -> let key = kvp.Key let term = !kvp.Value - let typ = TypeOf term - (key, Some term, typ)) - Memory.NewStackFrame state null (List.ofSeq frame) + Memory.Allocate targetModel.state key term) let defaultValues = Dictionary() encodingCache.regionConstants |> Seq.iter (fun kvp -> let region, fields = kvp.Key let constant = kvp.Value - let arr = m.Eval(constant, false) + let arr = z3Model.Eval(constant, false) let rec parseArray (arr : Expr) = if arr.IsConstantArray then assert(arr.Args.Length = 1) @@ -699,26 +699,29 @@ module internal Z3 = let address = arr.Args |> Array.last |> x.DecodeConcreteHeapAddress t |> ConcreteHeapAddress HeapRef address t let address = fields |> List.fold (fun address field -> StructField(address, field)) address - let states = Memory.WriteSafe state (Ref address) value - assert(states.Length = 1 && states.[0] = state) + // Seems that it's ok to use it with existing state + let states = Memory.WriteSafe targetModel.state (Ref address) value + // And that also will be true? + assert(states.Length = 1 && states.[0] = targetModel.state) elif arr.IsConst then () else internalfailf "Unexpected array expression in model: %O" arr parseArray arr) defaultValues |> Seq.iter (fun kvp -> let region = kvp.Key let constantValue = !kvp.Value - Memory.FillRegion state constantValue region) + // Also ok? + Memory.FillRegion targetModel.state constantValue region) encodingCache.heapAddresses |> Seq.iter (fun kvp -> let typ, _ = kvp.Key let addr = kvp.Value - if VectorTime.less addr VectorTime.zero && not <| PersistentDict.contains addr state.allocatedTypes then - state.allocatedTypes <- PersistentDict.add addr typ state.allocatedTypes) - state.startingTime <- [encodingCache.lastSymbolicAddress - 1] + if VectorTime.less addr VectorTime.zero && not <| PersistentDict.contains addr targetModel.state.allocatedTypes then + targetModel.state.allocatedTypes <- PersistentDict.add addr typ targetModel.state.allocatedTypes) + // Vector.time min of [encodingCache.lastSymbolicAddress - 1] and current starting time + targetModel.state.startingTime <- VectorTime.min targetModel.state.startingTime [encodingCache.lastSymbolicAddress - 1] encodingCache.heapAddresses.Clear() - {state = state; subst = subst; complete = true} - + encodingCache.t2e.Clear() let private ctx = new Context() let private builder = Z3Builder(ctx) @@ -785,12 +788,13 @@ module internal Z3 = match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model - let model = builder.MkModel z3Model + let updatedModel = {q.currentModel with state = {q.currentModel.state with model = q.currentModel.state.model}} + builder.UpdateModel z3Model updatedModel // let usedPaths = // pathAtoms // |> Seq.filter (fun atom -> z3Model.Eval(atom, false).IsTrue) // |> Seq.map (fun atom -> paths.[atom]) - SmtSat { mdl = model; usedPaths = [](*usedPaths*) } + SmtSat { mdl = updatedModel; usedPaths = [](*usedPaths*) } | Status.UNSATISFIABLE -> SmtUnsat { core = Array.empty (*optCtx.UnsatCore |> Array.map (builder.Decode Bool)*) } | Status.UNKNOWN -> From e24771569ab015dc802305e984fb9811bf2acf50 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sun, 21 Nov 2021 18:58:35 +0300 Subject: [PATCH 17/88] Implement SubTerms for heapReading --- VSharp.SILI.Core/Memory.fs | 6 +++++- VSharp.SILI.Core/MemoryRegion.fs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index aa2b90add..fe83dacde 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -206,7 +206,11 @@ module internal Memory = type private heapReading<'key, 'reg when 'key : equality and 'key :> IMemoryKey<'key, 'reg> and 'reg : equality and 'reg :> IRegion<'reg>> = {picker : regionPicker<'key, 'reg>; key : 'key; memoryObject : memoryRegion<'key, 'reg>; time : vectorTime} interface IMemoryAccessConstantSource with - override x.SubTerms = Seq.empty + override x.SubTerms = + let addKeySubTerms _ (regionKey : updateTreeKey<'key, term>) acc = + Seq.fold PersistentSet.add acc regionKey.key.SubTerms + RegionTree.foldr addKeySubTerms PersistentSet.empty x.memoryObject.updates + |> PersistentSet.toSeq override x.Time = x.time override x.TypeOfLocation = x.picker.sort.TypeOfLocation override x.IndependentWith otherSource = diff --git a/VSharp.SILI.Core/MemoryRegion.fs b/VSharp.SILI.Core/MemoryRegion.fs index 5b5b614ab..82fffec8d 100644 --- a/VSharp.SILI.Core/MemoryRegion.fs +++ b/VSharp.SILI.Core/MemoryRegion.fs @@ -11,6 +11,7 @@ type IMemoryKey<'a, 'reg when 'reg :> IRegion<'reg>> = abstract Map : (term -> term) -> (symbolicType -> symbolicType) -> (vectorTime -> vectorTime) -> 'reg -> 'reg * 'a abstract IsUnion : bool abstract Unguard : (term * 'a) list + abstract SubTerms : seq type regionSort = | HeapFieldSort of fieldId @@ -63,6 +64,8 @@ type heapAddressKey = newReg, {address = mapTerm x.address} override x.IsUnion = isUnion x.address override x.Unguard = Merging.unguard x.address |> List.map (fun (g, addr) -> (g, {address = addr})) + override x.SubTerms = Seq.singleton x.address + interface IComparable with override x.CompareTo y = match y with @@ -84,6 +87,8 @@ type heapArrayIndexKey = reg.Map (fun x -> x.Map mapTime) id, {address = mapTerm x.address; indices = List.map mapTerm x.indices} override x.IsUnion = isUnion x.address override x.Unguard = Merging.unguard x.address |> List.map (fun (g, addr) -> (g, {address = addr; indices = x.indices})) // TODO: if x.indices is the union of concrete values, then unguard indices as well + override x.SubTerms = x.address :: x.indices |> List.toSeq + interface IComparable with override x.CompareTo y = match y with @@ -108,6 +113,7 @@ type heapVectorIndexKey = reg.Map (fun x -> x.Map mapTime) id, {address = mapTerm x.address; index = mapTerm x.index} override x.IsUnion = isUnion x.address override x.Unguard = Merging.unguard x.address |> List.map (fun (g, addr) -> (g, {address = addr; index = x.index})) // TODO: if x.index is the union of concrete values, then unguard index as well + override x.SubTerms = [x.address; x.index] |> List.toSeq interface IComparable with override x.CompareTo y = match y with @@ -134,6 +140,8 @@ type stackBufferIndexKey = match x.index.term with | Union gvs when List.forall (fst >> isConcrete) gvs -> gvs |> List.map (fun (g, idx) -> (g, {index = idx})) | _ -> [(True, x)] + override x.SubTerms = Seq.singleton x.index + interface IComparable with override x.CompareTo y = match y with @@ -151,6 +159,8 @@ type symbolicTypeKey = reg.Map mapper, {typ = mapper x.typ} override x.IsUnion = false override x.Unguard = [(True, x)] + override x.SubTerms = Seq.empty + override x.ToString() = x.typ.ToString() type updateTreeKey<'key, 'value when 'key : equality> = From 08d1672be51278a7139262be7c61a7d294c20331 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Thu, 25 Nov 2021 23:44:34 +0300 Subject: [PATCH 18/88] Fix subterms for heapReading and add Slice and Union to discoverConstants --- VSharp.SILI.Core/Memory.fs | 8 ++++++-- VSharp.SILI.Core/Terms.fs | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index fe83dacde..1ebe9507e 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -209,8 +209,11 @@ module internal Memory = override x.SubTerms = let addKeySubTerms _ (regionKey : updateTreeKey<'key, term>) acc = Seq.fold PersistentSet.add acc regionKey.key.SubTerms - RegionTree.foldr addKeySubTerms PersistentSet.empty x.memoryObject.updates + let subterms = + RegionTree.foldr addKeySubTerms PersistentSet.empty x.memoryObject.updates |> PersistentSet.toSeq + |> Seq.append x.key.SubTerms + subterms override x.Time = x.time override x.TypeOfLocation = x.picker.sort.TypeOfLocation override x.IndependentWith otherSource = @@ -661,7 +664,8 @@ module internal Memory = let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = let keepIndependentWith pc cond = - PC.fragments pc + let fragments = PC.fragments pc + fragments |> Seq.tryFind (PC.toSeq >> Seq.contains cond) |> function | Some(fragment) -> fragment diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index 81bb814c3..350810020 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -649,6 +649,10 @@ module internal Terms = doFold folder state indent | GuardedValues(gs, vs) -> foldSeq folder gs state |> foldSeq folder vs + | Slice(t1, t2, t3, t4) -> + foldSeq folder [t1; t2; t3; t4] state + | Union(terms) -> + foldSeq folder (List.unzip terms |> (fun (l1, l2) -> List.append l1 l2)) state | _ -> state and doFold folder state term = From 0b9188d84026212486730bbceee61cf66dcbd082 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 1 Dec 2021 21:40:53 +0300 Subject: [PATCH 19/88] Prettify code --- VSharp.SILI.Core/Memory.fs | 26 +++++----- VSharp.SILI.Core/PathCondition.fs | 48 +++++++++---------- VSharp.Solver/Z3.fs | 5 -- .../Tests/ConstraintIndependenceSandbox.cs | 40 ---------------- 4 files changed, 36 insertions(+), 83 deletions(-) delete mode 100644 VSharp.Test/Tests/ConstraintIndependenceSandbox.cs diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index ebf09f5f5..737140451 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -210,11 +210,9 @@ module internal Memory = override x.SubTerms = let addKeySubTerms _ (regionKey : updateTreeKey<'key, term>) acc = Seq.fold PersistentSet.add acc regionKey.key.SubTerms - let subterms = - RegionTree.foldr addKeySubTerms PersistentSet.empty x.memoryObject.updates - |> PersistentSet.toSeq - |> Seq.append x.key.SubTerms - subterms + RegionTree.foldr addKeySubTerms PersistentSet.empty x.memoryObject.updates + |> PersistentSet.toSeq + |> Seq.append x.key.SubTerms override x.Time = x.time override x.TypeOfLocation = x.picker.sort.TypeOfLocation override x.IndependentWith otherSource = @@ -664,13 +662,13 @@ module internal Memory = commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let keepIndependentWith pc cond = - let fragments = PC.fragments pc - fragments + // Returns PC containing only constraints dependent with cond + let keepDependentWith pc cond = + PC.fragments pc |> Seq.tryFind (PC.toSeq >> Seq.contains cond) |> function | Some(fragment) -> fragment - | None -> pc + | None -> pc let execution thenState elseState condition k = assert (condition <> True && condition <> False) thenBranch thenState (fun thenResult -> @@ -680,9 +678,9 @@ module internal Memory = let negatedCondition = !!condition let thenPc = PC.add state.pc condition let elsePc = PC.add state.pc negatedCondition - let independentThenPc = keepIndependentWith thenPc condition - // Unnecessary - let independentElsePc = keepIndependentWith elsePc negatedCondition + let independentThenPc = keepDependentWith thenPc condition + // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond + let independentElsePc = keepDependentWith elsePc negatedCondition if PC.isFalse independentThenPc then conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) @@ -713,11 +711,11 @@ module internal Memory = | SolverInteraction.SmtUnknown _ -> conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model2 -> + | SolverInteraction.SmtSat model -> conditionState.pc <- elsePc let thenState = conditionState let elseState = copy conditionState elsePc - elseState.model <- Some model2.mdl + elseState.model <- Some model.mdl thenState.pc <- thenPc execution thenState elseState condition k) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index bba1955b4..17ceaaf36 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -93,29 +93,29 @@ module internal PC = /// one path condition are independent with constants contained in another one /// let public fragments pc = + let groupConditionsByUnionFindParent groups cond constant = + let parent = + constant + |> function + | Some(someConst) -> PersistentUnionFind.tryFind someConst pc.constants + | None -> None + let updatedGroup = + PersistentDict.tryFind groups parent + |> function + | Some(condSet) -> PersistentSet.add condSet cond + | None -> PersistentSet.add PersistentSet.empty cond + PersistentDict.add parent updatedGroup groups + + let conditionsGroupToPathCondition (parent, conds) = + let fragmentConstants = + match parent with + | Some(parent) -> PersistentUnionFind.subset parent pc.constants + | None -> PersistentUnionFind.empty + let fragmentConditionsWithConstants = + PersistentSet.fold (fun dict cond -> PersistentDict.add cond parent dict) PersistentDict.empty conds + {constants = fragmentConstants; conditionsWithConstants = fragmentConditionsWithConstants} + pc.conditionsWithConstants - |> PersistentDict.fold - (fun groups cond constant -> - let parent = - constant - |> function - | Some(someConst) -> PersistentUnionFind.tryFind someConst pc.constants - | None -> None - let updatedGroup = - PersistentDict.tryFind groups parent - |> function - | Some(condSet) -> PersistentSet.add condSet cond - | None -> PersistentSet.add PersistentSet.empty cond - PersistentDict.add parent updatedGroup groups - ) PersistentDict.empty + |> PersistentDict.fold groupConditionsByUnionFindParent PersistentDict.empty |> PersistentDict.toSeq - |> Seq.map - (fun (parent, conds) -> - let fragmentConstants = - match parent with - | Some(parent) -> PersistentUnionFind.subset parent pc.constants - | None -> PersistentUnionFind.empty - let fragmentConditionsWithConstants = - PersistentSet.fold (fun dict cond -> PersistentDict.add cond parent dict) PersistentDict.empty conds - {constants = fragmentConstants; conditionsWithConstants = fragmentConditionsWithConstants} - ) \ No newline at end of file + |> Seq.map conditionsGroupToPathCondition \ No newline at end of file diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 2d03c92af..25df928e6 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -673,7 +673,6 @@ module internal Z3 = if (Memory.IsStackEmpty targetModel.state) then Memory.NewStackFrame targetModel.state null List.empty - // Check that stack is not empty stackEntries |> Seq.iter (fun kvp -> let key = kvp.Key let term = !kvp.Value @@ -707,9 +706,7 @@ module internal Z3 = let address = arr.Args |> Array.last |> x.DecodeConcreteHeapAddress t |> ConcreteHeapAddress HeapRef address t let address = fields |> List.fold (fun address field -> StructField(address, field)) address - // Seems that it's ok to use it with existing state let states = Memory.WriteSafe targetModel.state (Ref address) value - // And that also will be true? assert(states.Length = 1 && states.[0] = targetModel.state) elif arr.IsConst then () else internalfailf "Unexpected array expression in model: %O" arr @@ -717,7 +714,6 @@ module internal Z3 = defaultValues |> Seq.iter (fun kvp -> let region = kvp.Key let constantValue = !kvp.Value - // Also ok? Memory.FillRegion targetModel.state constantValue region) encodingCache.heapAddresses |> Seq.iter (fun kvp -> @@ -725,7 +721,6 @@ module internal Z3 = let addr = kvp.Value if VectorTime.less addr VectorTime.zero && not <| PersistentDict.contains addr targetModel.state.allocatedTypes then targetModel.state.allocatedTypes <- PersistentDict.add addr typ targetModel.state.allocatedTypes) - // Vector.time min of [encodingCache.lastSymbolicAddress - 1] and current starting time targetModel.state.startingTime <- VectorTime.min targetModel.state.startingTime [encodingCache.lastSymbolicAddress - 1] encodingCache.heapAddresses.Clear() diff --git a/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs b/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs deleted file mode 100644 index 7655097b8..000000000 --- a/VSharp.Test/Tests/ConstraintIndependenceSandbox.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace VSharp.Test.Tests -{ - [TestSvmFixture] - public class ConstraintIndependenceSandbox - { - [TestSvm] - public static int Sandbox(int a, int b, int c) - { - if (c > 0) - { - if (a > 100) - { - if (b == c) - { - return 1; - } - else - { - return 2; - } - } - else - { - if (b < 150) - { - return 3; - } - else - { - return 4; - } - } - } - else - { - return 5; - } - } - } -} \ No newline at end of file From 66756cc2dc9b396650493029c13b3e96773635eb Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 3 Dec 2021 11:39:45 +0300 Subject: [PATCH 20/88] [style] PR comments fixes --- VSharp.SILI.Core/Memory.fs | 4 +- VSharp.SILI.Core/PathCondition.fs | 83 +++++++++++++++-------------- VSharp.Utils/PersistentUnionFind.fs | 2 +- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 737140451..4a5952aff 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -666,9 +666,7 @@ module internal Memory = let keepDependentWith pc cond = PC.fragments pc |> Seq.tryFind (PC.toSeq >> Seq.contains cond) - |> function - | Some(fragment) -> fragment - | None -> pc + |> Option.defaultValue pc let execution thenState elseState condition k = assert (condition <> True && condition <> False) thenBranch thenState (fun thenResult -> diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 17ceaaf36..99afdb360 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -35,47 +35,52 @@ module internal PC = let private someSetElement = PersistentSet.toSeq >> Seq.tryHead + let private constSourcesIndependent = + function + | ConstantT(_, oneSrc, _), ConstantT(_, anotherSrc, _) -> oneSrc.IndependentWith anotherSrc + | _ -> true + + let private addWithMerge pc cond : pathCondition = + let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq + + let pufWithNewConsts = + condConsts + |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind t pc.constants) + |> PersistentSet.fold PersistentUnionFind.add pc.constants + + // Merge sets of constants dependent in terms of ISymbolicConstantSource + let pufMergedByConstantSource = + Seq.allPairs + (condConsts |> PersistentSet.toSeq) + (pc.constants |> PersistentUnionFind.toSeq) + |> Seq.filter (constSourcesIndependent >> not) + |> Seq.fold (fun puf (const1, const2) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts + + (* + Merge sets of constants dependent in terms of reachability in graph where + edge between constants means that they are contained in the same condition + *) + let pufMergedByDependentCondition = + condConsts + |> someSetElement + |> function + | Some(parent) -> + PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts + | None -> pufMergedByConstantSource + + {constants = pufMergedByDependentCondition + conditionsWithConstants = + condConsts + |> someSetElement + |> (fun head -> PersistentDict.add cond head pc.conditionsWithConstants)} + let public add pc cond : pathCondition = match cond with | True -> pc | False -> falsePC | _ when isFalse pc -> falsePC | _ when PersistentDict.contains !!cond pc.conditionsWithConstants -> falsePC - | _ -> - let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq - let pufWithNewConsts = - condConsts - |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind t pc.constants) - |> PersistentSet.fold PersistentUnionFind.add pc.constants - let constsWithSources = - Seq.map - (function - | ConstantT(_, src, _) as constant -> constant, src - | _ -> __unreachable__() - ) - // Merge sets of constants dependent in terms of ISymbolicConstantSource - let pufMergedByConstantSource = - Seq.allPairs - (condConsts |> PersistentSet.toSeq |> constsWithSources) - (pc.constants |> PersistentUnionFind.toSeq |> constsWithSources) - |> Seq.filter (fun ((_, src1), (_, src2)) -> not <| src1.IndependentWith src2) - |> Seq.fold (fun puf ((const1, _), (const2, _)) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts - (* - Merge sets of constants dependent in terms of reachability in graph where - edge between constants means that they are contained in the same condition - *) - let pufMergedByDependentCondition = - condConsts - |> someSetElement - |> function - | Some(parent) -> - PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts - | None -> pufMergedByConstantSource - {constants = pufMergedByDependentCondition - conditionsWithConstants = - condConsts - |> someSetElement - |> (fun head -> PersistentDict.add cond head pc.conditionsWithConstants)} + | _ -> addWithMerge pc cond let public mapPC mapper (pc : pathCondition) : pathCondition = let mapAndAdd acc cond k = @@ -94,11 +99,7 @@ module internal PC = /// let public fragments pc = let groupConditionsByUnionFindParent groups cond constant = - let parent = - constant - |> function - | Some(someConst) -> PersistentUnionFind.tryFind someConst pc.constants - | None -> None + let parent = constant |> Option.bind (fun constant -> PersistentUnionFind.tryFind constant pc.constants) let updatedGroup = PersistentDict.tryFind groups parent |> function @@ -118,4 +119,4 @@ module internal PC = pc.conditionsWithConstants |> PersistentDict.fold groupConditionsByUnionFindParent PersistentDict.empty |> PersistentDict.toSeq - |> Seq.map conditionsGroupToPathCondition \ No newline at end of file + |> Seq.map conditionsGroupToPathCondition diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index b9492b7d1..289c111dc 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -96,4 +96,4 @@ module public PersistentUnionFind = traverse unwrappedNext updatedDict else updatedDict let subsetElements = traverse a PersistentDict.empty - {elements = subsetElements} \ No newline at end of file + {elements = subsetElements} From 4f3a43164d4450d570b9ef361df31ca3425e1803 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 3 Dec 2021 13:48:02 +0300 Subject: [PATCH 21/88] [fix] Fix PC check for False --- VSharp.SILI.Core/PathCondition.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 99afdb360..c9d78619e 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -29,7 +29,7 @@ module internal PC = conditionsWithConstants = PersistentDict.add False None PersistentDict.empty} let public isFalse pc = - let isFalsePC = PersistentDict.contains False pc.conditionsWithConstants + let isFalsePC = pc.conditionsWithConstants |> PersistentDict.keys |> Seq.contains False if isFalsePC then assert(toSeq pc |> Seq.length = 1) isFalsePC From c9dbb4adef44fd7fd7bc09895216735d9bea4a08 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 3 Dec 2021 14:01:07 +0300 Subject: [PATCH 22/88] [style] Use Option.defaultValue and rename slice args --- VSharp.SILI.Core/PathCondition.fs | 4 ++-- VSharp.SILI.Core/SolverInteraction.fs | 7 +++---- VSharp.SILI.Core/Terms.fs | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index c9d78619e..178c680c6 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -80,8 +80,8 @@ module internal PC = | False -> falsePC | _ when isFalse pc -> falsePC | _ when PersistentDict.contains !!cond pc.conditionsWithConstants -> falsePC - | _ -> addWithMerge pc cond - + | _ -> addWithMerge pc cond + let public mapPC mapper (pc : pathCondition) : pathCondition = let mapAndAdd acc cond k = let acc' = mapper cond |> add acc diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index b92924631..63ee4b2c3 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -41,9 +41,8 @@ module public SolverInteraction = let formula = PC.toSeq state.pc |> conjunction match solver with | Some s -> - let model = - match state.model with - | Some(m) -> m - | None -> { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } + let model = + state.model + |> Option.defaultValue { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } | None -> SmtUnknown "" diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index 350810020..83847185b 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -649,8 +649,8 @@ module internal Terms = doFold folder state indent | GuardedValues(gs, vs) -> foldSeq folder gs state |> foldSeq folder vs - | Slice(t1, t2, t3, t4) -> - foldSeq folder [t1; t2; t3; t4] state + | Slice(term, first, termSize, pos) -> + foldSeq folder [term; first; termSize; pos] state | Union(terms) -> foldSeq folder (List.unzip terms |> (fun (l1, l2) -> List.append l1 l2)) state | _ -> state From 8abad91dfbfa63887f6d81744281e5a637652592 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 24 Dec 2021 14:55:21 +0300 Subject: [PATCH 23/88] Add StatedLogger and clear cache after model update --- VSharp.SILI.Core/Memory.fs | 29 ++++++++++++++++++--------- VSharp.SILI.Core/SolverInteraction.fs | 2 ++ VSharp.SILI.Core/State.fs | 2 ++ VSharp.SILI/Interpreter.fs | 4 +++- VSharp.SILI/TestGenerator.fs | 1 + VSharp.Solver/Z3.fs | 9 +++++++-- VSharp.Utils/StatedLogger.fs | 20 ++++++++++++++++++ VSharp.Utils/UnitTest.fs | 8 ++++++++ VSharp.Utils/UnitTests.fs | 2 ++ VSharp.Utils/VSharp.Utils.fsproj | 1 + 10 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 VSharp.Utils/StatedLogger.fs diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 4a5952aff..e9c75a432 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -30,7 +30,7 @@ module internal Memory = match memoryMode with | ConcreteMemory -> ConcreteMemory.deepCopy state | SymbolicMemory -> state - { state with pc = newPc } + { state with pc = newPc; id = Guid.NewGuid().ToString() } let private isZeroAddress (x : concreteHeapAddress) = x = VectorTime.zero @@ -672,7 +672,7 @@ module internal Memory = thenBranch thenState (fun thenResult -> elseBranch elseState (fun elseResult -> merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> + conditionInvocation state (fun (condition, conditionState) -> let negatedCondition = !!condition let thenPc = PC.add state.pc condition let elsePc = PC.add state.pc negatedCondition @@ -695,26 +695,36 @@ module internal Memory = | SolverInteraction.SmtUnknown _ -> __insufficientInformation__ "Unable to witness branch" | SolverInteraction.SmtSat model -> - conditionState.pc <- thenPc + conditionState.pc <- elsePc conditionState.model <- Some model.mdl + StatedLogger.log conditionState.id $"Model stack: %s{model.mdl.state.stack.frames.ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> conditionState.pc <- elsePc + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model -> + | SolverInteraction.SmtSat thenModel -> conditionState.pc <- independentElsePc - conditionState.model <- Some model.mdl match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> conditionState.pc <- thenPc + conditionState.model <- Some thenModel.mdl + StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model -> - conditionState.pc <- elsePc + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- thenPc let thenState = conditionState let elseState = copy conditionState elsePc - elseState.model <- Some model.mdl - thenState.pc <- thenPc + StatedLogger.copy thenState.id elseState.id + StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" + StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + elseState.model <- Some elseModel.mdl + thenState.model <- Some thenModel.mdl execution thenState elseState condition k) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = @@ -1509,6 +1519,7 @@ module internal Memory = let g = g1 &&& g2 &&& g3 &&& g4 &&& g5 &&& g6 if not <| isFalse g then return { + id = Guid.NewGuid().ToString() pc = if isTrue g then pc else PC.add pc g evaluationStack = evaluationStack exceptionsRegister = exceptionRegister diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 63ee4b2c3..52b788ccb 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -44,5 +44,7 @@ module public SolverInteraction = let model = state.model |> Option.defaultValue { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } + StatedLogger.log state.id $"Counter: %s{counter.ToString()}" s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } | None -> SmtUnknown "" + \ No newline at end of file diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 5a6597951..64ad7d2b5 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -115,6 +115,7 @@ with and [] state = { + id : string mutable pc : pathCondition mutable evaluationStack : evaluationStack mutable stack : callStack // Arguments and local variables @@ -145,6 +146,7 @@ and module public State = let makeEmpty() = { + id = Guid.NewGuid().ToString() pc = PC.empty evaluationStack = EvaluationStack.empty exceptionsRegister = NoException diff --git a/VSharp.SILI/Interpreter.fs b/VSharp.SILI/Interpreter.fs index d35ef2370..41c8e4b2a 100644 --- a/VSharp.SILI/Interpreter.fs +++ b/VSharp.SILI/Interpreter.fs @@ -1931,7 +1931,9 @@ type internal ILInterpreter() as this = let rec makeStep' ip k = match ip with | Instruction(offset, m) -> - if offset = 0 then Logger.printLogLazy Logger.Info "Starting to explore method %O" (lazy Reflection.getFullMethodName m) + if offset = 0 then + Logger.printLogLazy Logger.Info "Starting to explore method %O" (lazy Reflection.getFullMethodName m) + StatedLogger.log cilState.state.id $"In method: %O{Reflection.getFullMethodName m}" x.ExecuteInstruction m offset cilState |> k | Exit m -> exit m diff --git a/VSharp.SILI/TestGenerator.fs b/VSharp.SILI/TestGenerator.fs index b414aa369..27fb231ab 100644 --- a/VSharp.SILI/TestGenerator.fs +++ b/VSharp.SILI/TestGenerator.fs @@ -185,6 +185,7 @@ module TestGenerator = let state2test isError (m : MethodBase) cmdArgs (cilState : cilState) = let indices = Dictionary() let test = UnitTest m + test.StateId <- Some cilState.state.id test.AddExtraAssemblySearchPath (Directory.GetCurrentDirectory()) let hasException = match cilState.state.exceptionsRegister with diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 25df928e6..d08d65723 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -9,7 +9,7 @@ open VSharp.Core.SolverInteraction open Logger module internal Z3 = - + // ------------------------------- Exceptions ------------------------------- type EncodingException(msg : string) = @@ -84,6 +84,9 @@ module internal Z3 = member x.Reset() = encodingCache <- freshCache() + + member x.ClearT2E() = + encodingCache.t2e.Clear() member private x.ValidateId id = assert(not <| String.IsNullOrWhiteSpace id) @@ -646,7 +649,6 @@ module internal Z3 = Memory.DefaultOf structureType |> ref) structureRef := x.WriteFields !structureRef value fields - member x.UpdateModel (z3Model : Model) (targetModel : model) = let stackEntries = Dictionary() encodingCache.t2e |> Seq.iter (fun kvp -> @@ -797,10 +799,13 @@ module internal Z3 = // pathAtoms // |> Seq.filter (fun atom -> z3Model.Eval(atom, false).IsTrue) // |> Seq.map (fun atom -> paths.[atom]) + builder.ClearT2E() SmtSat { mdl = updatedModel; usedPaths = [](*usedPaths*) } | Status.UNSATISFIABLE -> + builder.ClearT2E() SmtUnsat { core = Array.empty (*optCtx.UnsatCore |> Array.map (builder.Decode Bool)*) } | Status.UNKNOWN -> + builder.ClearT2E() SmtUnknown optCtx.ReasonUnknown | _ -> __unreachable__() with diff --git a/VSharp.Utils/StatedLogger.fs b/VSharp.Utils/StatedLogger.fs new file mode 100644 index 000000000..6c32a2cf5 --- /dev/null +++ b/VSharp.Utils/StatedLogger.fs @@ -0,0 +1,20 @@ +namespace VSharp + +open System +open System.Collections.Generic +open System.IO +open System.Text + +module StatedLogger = + let stateLogs = Dictionary() + let public log stateId message = + if not <| stateLogs.ContainsKey(stateId) then stateLogs.Add(stateId, StringBuilder()) + stateLogs.[stateId].AppendLine(message) |> ignore + + let public copy fromStateId toStateId = + let from = stateLogs.GetValueOrDefault(fromStateId, StringBuilder()).ToString() + stateLogs.[toStateId] <- StringBuilder().AppendLine(from) + + let public saveLog stateId path = + let log = stateLogs.GetValueOrDefault(stateId, StringBuilder()).ToString() + File.WriteAllText(path, log) \ No newline at end of file diff --git a/VSharp.Utils/UnitTest.fs b/VSharp.Utils/UnitTest.fs index 1d12d0349..f6f51b557 100644 --- a/VSharp.Utils/UnitTest.fs +++ b/VSharp.Utils/UnitTest.fs @@ -55,8 +55,16 @@ type UnitTest private (m : MethodBase, info : testInfo) = let classTypeParameters = info.classTypeParameters |> Array.map Serialization.decodeType let methodTypeParameters = info.methodTypeParameters |> Array.map Serialization.decodeType let mutable extraAssemblyLoadDirs : string list = [] + + let mutable stateId : string option = None + new(m : MethodBase) = UnitTest(m, testInfo.OfMethod m) + + member x.StateId + with get() = stateId + and set id = + stateId <- id member x.Method with get() = m member x.ThisArg diff --git a/VSharp.Utils/UnitTests.fs b/VSharp.Utils/UnitTests.fs index 5f7848453..28093c138 100644 --- a/VSharp.Utils/UnitTests.fs +++ b/VSharp.Utils/UnitTests.fs @@ -32,6 +32,8 @@ type UnitTests(outputDir : string) = member x.GenerateTest (test : UnitTest) = testNumber <- testNumber + 1u + if test.StateId.IsSome then + StatedLogger.saveLog test.StateId.Value $"%s{currentDir.FullName}%c{Path.DirectorySeparatorChar}info%s{testNumber.ToString()}.txt" generateTest test ("test" + testNumber.ToString()) member x.GenerateError (test : UnitTest) = diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 409efa7ff..88bbe7d22 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -29,6 +29,7 @@ + From 7251d4e65d60ca6ef4d09693a9e7e14856c980c5 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 24 Dec 2021 17:48:25 +0300 Subject: [PATCH 24/88] Fill model with this and params --- VSharp.SILI.Core/API.fs | 4 +++- VSharp.SILI.Core/API.fsi | 3 ++- VSharp.SILI.Core/Memory.fs | 15 +++++++++++++++ VSharp.SILI.Core/SolverInteraction.fs | 3 +-- VSharp.SILI.Core/State.fs | 8 +++++--- VSharp.SILI/SILI.fs | 10 +++++++--- VSharp.SILI/TestGenerator.fs | 20 +++++--------------- VSharp.Solver/Z3.fs | 2 +- 8 files changed, 39 insertions(+), 26 deletions(-) diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 8e07445e1..890c02c6a 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -1,5 +1,6 @@ namespace VSharp.Core +open System.Reflection open FSharpx.Collections open VSharp @@ -237,7 +238,7 @@ module API = let EmptyStack = EvaluationStack.empty module public Memory = - let EmptyState() = State.makeEmpty() + let EmptyState modelState = State.makeEmpty modelState let PopFrame state = Memory.popFrame state let ForcePopFrames count state = Memory.forcePopFrames count state let PopTypeVariables state = Memory.popTypeVariablesSubstitution state @@ -339,6 +340,7 @@ module API = let MakeSymbolicThis m = Memory.makeSymbolicThis m let MakeSymbolicValue source name typ = Memory.makeSymbolicValue source name typ + let FillWithParametersAndThis state method = Memory.fillWithParametersAndThis state method let CallStackContainsFunction state method = CallStack.containsFunc state.stack method let CallStackSize state = CallStack.size state.stack diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 82816261e..42e2e61de 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -196,7 +196,7 @@ module API = val EmptyStack : evaluationStack module public Memory = - val EmptyState : unit -> state + val EmptyState : state option -> state val PopFrame : state -> unit val ForcePopFrames : int -> state -> unit val PopTypeVariables : state -> unit @@ -231,6 +231,7 @@ module API = val MakeSymbolicThis : MethodBase -> term val MakeSymbolicValue : IMemoryAccessConstantSource -> string -> symbolicType -> term + val FillWithParametersAndThis : state -> MethodBase -> unit val CallStackContainsFunction : state -> MethodBase -> bool val CallStackSize : state -> int diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index e9c75a432..48763f3c9 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -2,6 +2,7 @@ namespace VSharp.Core open System open System.Collections.Generic +open System.Reflection open System.Text open FSharpx.Collections open VSharp @@ -349,6 +350,20 @@ module internal Memory = let declaringType = fromDotNetType m.DeclaringType if isValueType declaringType then __insufficientInformation__ "Can't execute in isolation methods of value types, because we can't be sure where exactly \"this\" is allocated!" else HeapRef (Constant "this" {baseSource = {key = ThisKey m; time = Some VectorTime.zero}} AddressType) declaringType + + let fillWithParametersAndThis state (method : MethodBase) = + let parameters = method.GetParameters() |> Seq.map (fun param -> + (ParameterKey param, None, fromDotNetType param.ParameterType)) |> List.ofSeq + let parametersAndThis = + if Reflection.hasThis method then + let t = fromDotNetType method.DeclaringType + let addr = [-1] + let thisRef = HeapRef (ConcreteHeapAddress addr) t + state.allocatedTypes <- PersistentDict.add addr t state.allocatedTypes + state.startingTime <- [-2] + (ThisKey method, Some thisRef, t) :: parameters // TODO: incorrect type when ``this'' is Ref to stack + else parameters + newStackFrame state method parametersAndThis // =============== Marshalling/unmarshalling without state changing =============== diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 52b788ccb..726b955a1 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -43,8 +43,7 @@ module public SolverInteraction = | Some s -> let model = state.model - |> Option.defaultValue { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } - StatedLogger.log state.id $"Counter: %s{counter.ToString()}" + |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } | None -> SmtUnknown "" \ No newline at end of file diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 64ad7d2b5..b18273cbf 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -2,6 +2,7 @@ namespace VSharp.Core open System open System.Collections.Generic +open System.Reflection open VSharp open VSharp.Core.Types.Constructor open VSharp.Utils @@ -144,8 +145,7 @@ and abstract Compose : state -> term module public State = - - let makeEmpty() = { + let makeEmpty modelState = { id = Guid.NewGuid().ToString() pc = PC.empty evaluationStack = EvaluationStack.empty @@ -166,5 +166,7 @@ module public State = delegates = PersistentDict.empty currentTime = [1] startingTime = VectorTime.zero - model = None + model = + Option.bind (fun state -> Some {subst = Dictionary<_,_>(); state = state; complete = true}) modelState } + diff --git a/VSharp.SILI/SILI.fs b/VSharp.SILI/SILI.fs index a8e043cba..029a78cc4 100644 --- a/VSharp.SILI/SILI.fs +++ b/VSharp.SILI/SILI.fs @@ -15,7 +15,7 @@ type public SILI(options : SiliOptions) = let statistics = SILIStatistics() let infty = UInt32.MaxValue - let emptyState = Memory.EmptyState() + let emptyState = Memory.EmptyState None let interpreter = ILInterpreter() let mutable entryIP : ip = Exit null let mutable reportFinished : cilState -> unit = fun _ -> internalfail "reporter not configured!" @@ -72,7 +72,9 @@ type public SILI(options : SiliOptions) = action.Invoke e static member private FormInitialStateWithoutStatics (method : MethodBase) = - let initialState = Memory.EmptyState() + let modelState = Memory.EmptyState None + Memory.FillWithParametersAndThis modelState method + let initialState = Memory.EmptyState (Some modelState) let cilState = makeInitialState method initialState try let this(*, isMethodOfStruct*) = @@ -188,7 +190,9 @@ type public SILI(options : SiliOptions) = reportIncomplete <- wrapOnIIE onIIE reportInternalFail <- wrapOnInternalFail onInternalFail interpreter.ConfigureErrorReporter reportError - let state = Memory.EmptyState() + let modelState = Memory.EmptyState None + Memory.FillWithParametersAndThis modelState method + let state = Memory.EmptyState (Some modelState) let argsToState args = let argTerms = Seq.map (fun str -> Memory.AllocateString str state) args let stringType = Types.FromDotNetType typeof diff --git a/VSharp.SILI/TestGenerator.fs b/VSharp.SILI/TestGenerator.fs index 27fb231ab..c84335264 100644 --- a/VSharp.SILI/TestGenerator.fs +++ b/VSharp.SILI/TestGenerator.fs @@ -201,22 +201,12 @@ module TestGenerator = model2test test isError hasException indices m model cmdArgs cilState | None when cilState.state.pc = EmptyPathCondition -> // NOTE: case when no branches occured - let emptyState = Memory.EmptyState() + let emptyState = Memory.EmptyState None emptyState.allocatedTypes <- cilState.state.allocatedTypes - let parameters = m.GetParameters() |> Seq.map (fun param -> - (ParameterKey param, None, Types.FromDotNetType param.ParameterType)) |> List.ofSeq - let parametersAndThis = - if Reflection.hasThis m then - let this = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(m.DeclaringType) - test.ThisArg <- this - let t = Types.FromDotNetType m.DeclaringType - let addr = [-1] - let thisRef = HeapRef (ConcreteHeapAddress addr) t - emptyState.allocatedTypes <- PersistentDict.add addr t emptyState.allocatedTypes - emptyState.startingTime <- [-2] - (ThisKey m, Some thisRef, t) :: parameters // TODO: incorrect type when ``this'' is Ref to stack - else parameters - Memory.NewStackFrame emptyState m parametersAndThis + if Reflection.hasThis m then + let this = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(m.DeclaringType) + test.ThisArg <- this + Memory.FillWithParametersAndThis emptyState m let parametersInfo = m.GetParameters() match cmdArgs with | Some args -> diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index d08d65723..69f436c48 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -72,7 +72,7 @@ module internal Z3 = type private Z3Builder(ctx : Context) = let mutable encodingCache = freshCache() - let emptyState = Memory.EmptyState() + let emptyState = Memory.EmptyState None let getMemoryConstant mkConst (typ : regionSort * fieldId list) = let result : ArrayExpr ref = ref null From 99f366d6cee34047aefc1bf9330fdf1681eacff3 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Fri, 24 Dec 2021 19:05:47 +0300 Subject: [PATCH 25/88] [fix] Fix namespace conflict --- VSharp.SILI.Core/Memory.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 48763f3c9..b500f3a3b 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -2,7 +2,6 @@ namespace VSharp.Core open System open System.Collections.Generic -open System.Reflection open System.Text open FSharpx.Collections open VSharp @@ -351,7 +350,7 @@ module internal Memory = if isValueType declaringType then __insufficientInformation__ "Can't execute in isolation methods of value types, because we can't be sure where exactly \"this\" is allocated!" else HeapRef (Constant "this" {baseSource = {key = ThisKey m; time = Some VectorTime.zero}} AddressType) declaringType - let fillWithParametersAndThis state (method : MethodBase) = + let fillWithParametersAndThis state (method : System.Reflection.MethodBase) = let parameters = method.GetParameters() |> Seq.map (fun param -> (ParameterKey param, None, fromDotNetType param.ParameterType)) |> List.ofSeq let parametersAndThis = From de31156f0b957047c9c448234e3849abe0b581ea Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sun, 26 Dec 2021 13:23:57 +0300 Subject: [PATCH 26/88] [style] Check if pc contains cond more prettily --- VSharp.SILI.Core/PathCondition.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 178c680c6..a98c0176e 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -29,7 +29,7 @@ module internal PC = conditionsWithConstants = PersistentDict.add False None PersistentDict.empty} let public isFalse pc = - let isFalsePC = pc.conditionsWithConstants |> PersistentDict.keys |> Seq.contains False + let isFalsePC = pc |> toSeq |> Seq.contains False if isFalsePC then assert(toSeq pc |> Seq.length = 1) isFalsePC @@ -79,7 +79,7 @@ module internal PC = | True -> pc | False -> falsePC | _ when isFalse pc -> falsePC - | _ when PersistentDict.contains !!cond pc.conditionsWithConstants -> falsePC + | _ when pc |> toSeq |> Seq.contains !!cond -> falsePC | _ -> addWithMerge pc cond let public mapPC mapper (pc : pathCondition) : pathCondition = From 46c76ba90857e13c2838ad12504a26e3f29b01e6 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 9 Feb 2022 13:16:41 +0300 Subject: [PATCH 27/88] [feat] Add Stopwatch module for method execution time measurement --- VSharp.SILI.Core/SolverInteraction.fs | 5 ++- VSharp.Test/IntegrationTests.cs | 2 + VSharp.Utils/Stopwatch.fs | 57 +++++++++++++++++++++++++++ VSharp.Utils/VSharp.Utils.fsproj | 1 + 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 VSharp.Utils/Stopwatch.fs diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index d0c32cb77..784e88083 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -38,5 +38,8 @@ module public SolverInteraction = let ctx = getEncodingContext state let formula = PC.toSeq state.pc |> conjunction match solver with - | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } + | Some s -> + Stopwatch.runMeasuringTime "checkSat" (fun () -> + s.CheckSat ctx {lvl = Level.zero; queryFml = formula } + ) | None -> SmtUnknown "" diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 599bb8ce1..dcb34099c 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -116,6 +116,8 @@ private TestResult Explore(TestExecutionContext context) { context.CurrentResult.SetResult(ResultState.Success); } + + Stopwatch.saveCurrentResults(unitTests.TestDirectory.FullName); } catch (Exception e) { diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs new file mode 100644 index 000000000..bb450f027 --- /dev/null +++ b/VSharp.Utils/Stopwatch.fs @@ -0,0 +1,57 @@ +namespace VSharp + +open System +open System.Collections.Generic +open System.Diagnostics +open System.IO + +module Stopwatch = + + type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } + + let private headerTag = "Tag" + let private headerTimesCalled = "Times called" + let private headerTime = "Total time (ms)" + + let private measurements = Dictionary() + + let public runMeasuringTime tag action = + let measurement = + if (measurements.ContainsKey tag) then + measurements.[tag] + else + let newMeasurement = { stopwatch = Stopwatch(); timesCalled = 0 } + measurements.[tag] <- newMeasurement + newMeasurement + try + measurement.stopwatch.Start() + measurement.timesCalled <- measurement.timesCalled + 1 + action() + finally + measurement.stopwatch.Stop() + + let public stopAll () = + measurements + |> Seq.map (|KeyValue|) + |> Seq.iter (fun (_, m) -> + m.stopwatch.Stop() + ) + + let private formatColumn (text : string) = + String.Format("{0,-20}", text) + + let private header = $"{formatColumn headerTag}|{formatColumn headerTimesCalled}|{formatColumn headerTime}\n" + + let public saveCurrentResults path = + let currentTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") + let filename = $"times-{currentTime}.txt" + stopAll() + let lines = + measurements + |> Seq.map (|KeyValue|) + |> Seq.map (fun (tag, m) -> + $"{formatColumn tag}|{formatColumn (m.timesCalled.ToString())}|{formatColumn (m.stopwatch.ElapsedMilliseconds.ToString())}" + ) + let lines = Seq.append (Seq.singleton header) lines + File.WriteAllLines($"{path}%c{Path.DirectorySeparatorChar}{filename}", lines) + \ No newline at end of file diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 823895747..80abd163b 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -28,6 +28,7 @@ + From c5f1e449b373edb66986223e5b6eafb06a5ffddd Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 14 Feb 2022 09:55:29 +0300 Subject: [PATCH 28/88] [feat] Add execution time writing to csv --- VSharp.SILI.Core/SolverInteraction.fs | 5 +- VSharp.Solver/Z3.fs | 5 +- VSharp.Test/IntegrationTests.cs | 2 +- VSharp.Utils/Stopwatch.fs | 73 +++++++++++++++++++++------ VSharp.Utils/VSharp.Utils.fsproj | 1 + 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 784e88083..234a38b2e 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -38,8 +38,5 @@ module public SolverInteraction = let ctx = getEncodingContext state let formula = PC.toSeq state.pc |> conjunction match solver with - | Some s -> - Stopwatch.runMeasuringTime "checkSat" (fun () -> - s.CheckSat ctx {lvl = Level.zero; queryFml = formula } - ) + | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } | None -> SmtUnknown "" diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index aa73623f9..a5509bf95 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -789,7 +789,10 @@ module internal Z3 = yield query.expr } |> Array.ofSeq // let pathAtoms = addSoftConstraints q.lvl - let result = optCtx.Check assumptions + let result = + Stopwatch.runMeasuringTime "Z3_check_sat" (fun () -> + optCtx.Check assumptions + ) match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index dcb34099c..159e7b392 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -117,7 +117,7 @@ private TestResult Explore(TestExecutionContext context) context.CurrentResult.SetResult(ResultState.Success); } - Stopwatch.saveCurrentResults(unitTests.TestDirectory.FullName); + Stopwatch.saveMeasurements(methodInfo.Name); } catch (Exception e) { diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index bb450f027..542b1e121 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -3,15 +3,27 @@ open System open System.Collections.Generic open System.Diagnostics +open System.Globalization open System.IO +open CsvHelper +open CsvHelper.Configuration module Stopwatch = type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } - - let private headerTag = "Tag" - let private headerTimesCalled = "Times called" - let private headerTime = "Total time (ms)" + type private csvRecord = + { + commitHash: string + dateTime: string + caseName: string + tag: string + timesCalled: int + totalTicks: int64 + totalMs: int64 + } + + let private csvPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VSharpBenchmark") + let private csvFilename = "benchmark.csv" let private measurements = Dictionary() @@ -37,21 +49,50 @@ module Stopwatch = m.stopwatch.Stop() ) - let private formatColumn (text : string) = - String.Format("{0,-20}", text) + let private getGitCommitHash () = + let procStartInfo = ProcessStartInfo("git", "rev-parse --short HEAD") + + procStartInfo.RedirectStandardOutput <- true + procStartInfo.UseShellExecute <- false + procStartInfo.CreateNoWindow <- true + + use proc = new Process() + proc.StartInfo <- procStartInfo + + proc.Start() |> ignore + proc.WaitForExit() - let private header = $"{formatColumn headerTag}|{formatColumn headerTimesCalled}|{formatColumn headerTime}\n" + proc.StandardOutput.ReadLine() - let public saveCurrentResults path = - let currentTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") - let filename = $"times-{currentTime}.txt" + let public saveMeasurements caseName = stopAll() - let lines = + + let commitHash = getGitCommitHash() + let currentDateTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") + + let records = measurements |> Seq.map (|KeyValue|) |> Seq.map (fun (tag, m) -> - $"{formatColumn tag}|{formatColumn (m.timesCalled.ToString())}|{formatColumn (m.stopwatch.ElapsedMilliseconds.ToString())}" - ) - let lines = Seq.append (Seq.singleton header) lines - File.WriteAllLines($"{path}%c{Path.DirectorySeparatorChar}{filename}", lines) - \ No newline at end of file + { + commitHash = commitHash + dateTime = currentDateTime + caseName = caseName + tag = tag + timesCalled = m.timesCalled + totalTicks = m.stopwatch.ElapsedTicks + totalMs = m.stopwatch.ElapsedMilliseconds + } + ) + + let targetPath = Path.Combine(csvPath, csvFilename) + + let configuration = CsvConfiguration(CultureInfo.InvariantCulture) + configuration.IncludePrivateMembers <- true + configuration.HasHeaderRecord <- File.Exists(targetPath) |> not + + Directory.CreateDirectory(csvPath) |> ignore + use streamWriter = File.AppendText(targetPath) + use csvWriter = new CsvWriter(streamWriter, configuration) + + csvWriter.WriteRecords(records) diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 80abd163b..fee6f9a3d 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -56,6 +56,7 @@ + From c27b432efbefdb677d6e13aa223c336bf7bfd932 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 14 Feb 2022 20:44:47 +0300 Subject: [PATCH 29/88] [feat] Measure total interpretation time --- VSharp.Test/IntegrationTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 159e7b392..be9f2d480 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Threading; +using Microsoft.FSharp.Core; using NUnit.Framework; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -86,7 +87,11 @@ private TestResult Explore(TestExecutionContext context) SILI explorer = new SILI(_options); UnitTests unitTests = new UnitTests(Directory.GetCurrentDirectory()); - explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); + Stopwatch.runMeasuringTime("total_interpretation", FuncConvert.FromAction(() => + { + explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, + _ => { }, e => throw e); + })); if (unitTests.UnitTestsCount == 0 && unitTests.ErrorsCount == 0 && explorer.Statistics.IncompleteStates.Count == 0) { From 487457d8a70ef976b4bedd1047a74aead6b07172 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Tue, 15 Feb 2022 23:09:32 +0300 Subject: [PATCH 30/88] Disable coverage and clear stopwatch after execution --- VSharp.Solver/Z3.fs | 6 ++++-- VSharp.Test/IntegrationTests.cs | 9 ++++++--- VSharp.Test/Tests/RegExTest.cs | 2 +- VSharp.Utils/CoverageTool.fs | 12 ++++++++---- VSharp.Utils/Stopwatch.fs | 2 ++ global.json | 5 +++++ 6 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 global.json diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index a5509bf95..a0d9f5f76 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -822,8 +822,10 @@ module internal Z3 = else let levelAtom = getLevelAtom lvl ctx.MkImplies(levelAtom, encoded) - optCtx.Assert(leveled) - + Stopwatch.runMeasuringTime "Z3_assert" (fun () -> + optCtx.Assert(leveled) + ) + member x.AddPath encCtx (p : path) = printLog Trace "SOLVER: [lvl %O] Asserting path:" p.lvl printLogLazy Trace " %s" (lazy(PathConditionToSeq p.state.pc |> Seq.map toString |> join " /\\ \n ")) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index be9f2d480..6da138599 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -90,7 +90,7 @@ private TestResult Explore(TestExecutionContext context) Stopwatch.runMeasuringTime("total_interpretation", FuncConvert.FromAction(() => { explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, - _ => { }, e => throw e); + _ => { }, e => throw e); })); if (unitTests.UnitTestsCount == 0 && unitTests.ErrorsCount == 0 && explorer.Statistics.IncompleteStates.Count == 0) @@ -106,7 +106,7 @@ private TestResult Explore(TestExecutionContext context) var coverageTool = new CoverageTool(unitTests.TestDirectory.FullName, Directory.GetCurrentDirectory()); coverageTool.Run(unitTests.TestDirectory); - int coverage = coverageTool.GetCoverage(methodInfo); + /*int coverage = coverageTool.GetCoverage(methodInfo); if (coverage != _expectedCoverage) { context.CurrentResult.SetResult(ResultState.Failure, @@ -115,7 +115,9 @@ private TestResult Explore(TestExecutionContext context) else { context.CurrentResult.SetResult(ResultState.Success); - } + }*/ + + context.CurrentResult.SetResult(ResultState.Success); } else { @@ -123,6 +125,7 @@ private TestResult Explore(TestExecutionContext context) } Stopwatch.saveMeasurements(methodInfo.Name); + Stopwatch.clear(); } catch (Exception e) { diff --git a/VSharp.Test/Tests/RegExTest.cs b/VSharp.Test/Tests/RegExTest.cs index 966185211..592f8a749 100644 --- a/VSharp.Test/Tests/RegExTest.cs +++ b/VSharp.Test/Tests/RegExTest.cs @@ -51,7 +51,7 @@ public static bool Match(string re, string text) public class RegExTest { [TestSvm] - public static string OwnImplementationTest(char c1, char c2, char c3, char c4, char c5, char c6) + public static string OwnImplementationTest1(char c1, char c2, char c3, char c4, char c5, char c6) { string pattern = new string(new char[] {c1, c2, c3}); string result = ""; diff --git a/VSharp.Utils/CoverageTool.fs b/VSharp.Utils/CoverageTool.fs index 0ce063745..0c606e9e0 100644 --- a/VSharp.Utils/CoverageTool.fs +++ b/VSharp.Utils/CoverageTool.fs @@ -26,7 +26,7 @@ type CoverageTool(testDir : string, runnerDir : string) = proc.WaitForExit() proc.ExitCode, output.ToString(), error.ToString() - let run = +(* let run = let _, localTools, _ = runDotnet "tool list" let globalArg = if localTools.Contains "dotcover" then "" @@ -37,7 +37,11 @@ type CoverageTool(testDir : string, runnerDir : string) = fun (directory : DirectoryInfo) -> let filters = ["-:module=Microsoft.*"; "-:module=FSharp.*"; "-:class=VSharp.*"; "-:module=VSharp.Utils"] let code, _, error = runDotnet <| sprintf "dotcover --dcFilters=\"%s\" %s%cVSharp.TestRunner.dll %s --dcReportType=DetailedXML %s" (filters |> join ";") runnerDir Path.DirectorySeparatorChar directory.FullName globalArg - code = 0, error + code = 0, error*) + + let run (directory : DirectoryInfo) = + let code, _, error = runDotnet <| sprintf " %s%cVSharp.TestRunner.dll %s" runnerDir Path.DirectorySeparatorChar directory.FullName + code = 0, error let mutable doc : XmlDocument = null @@ -74,8 +78,8 @@ type CoverageTool(testDir : string, runnerDir : string) = let success, error = run testDir if not success then raise <| InvalidOperationException ("Running dotCover failed: " + error) - doc <- XmlDocument() - doc.Load(sprintf "%s%cdotCover.Output.xml" testDir.FullName Path.DirectorySeparatorChar) +(* doc <- XmlDocument() + doc.Load(sprintf "%s%cdotCover.Output.xml" testDir.FullName Path.DirectorySeparatorChar)*) member x.GetCoverage (m : MethodInfo) = if String.IsNullOrEmpty m.DeclaringType.Namespace then diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index 542b1e121..501f87e2e 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -96,3 +96,5 @@ module Stopwatch = use csvWriter = new CsvWriter(streamWriter, configuration) csvWriter.WriteRecords(records) + + let public clear () = measurements.Clear() diff --git a/global.json b/global.json new file mode 100644 index 000000000..e86bfecd4 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "5.0.400" + } +} \ No newline at end of file From ab925c5d631c7a94e3b8f71aadad0e763b3c2ebf Mon Sep 17 00:00:00 2001 From: mxprshn Date: Wed, 23 Feb 2022 13:36:01 +0300 Subject: [PATCH 31/88] Measure model update and PC operations time --- VSharp.SILI.Core/PathCondition.fs | 60 ++++++++++++++++--------------- VSharp.Solver/Z3.fs | 4 ++- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index a98c0176e..16be7fb62 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -75,12 +75,14 @@ module internal PC = |> (fun head -> PersistentDict.add cond head pc.conditionsWithConstants)} let public add pc cond : pathCondition = - match cond with - | True -> pc - | False -> falsePC - | _ when isFalse pc -> falsePC - | _ when pc |> toSeq |> Seq.contains !!cond -> falsePC - | _ -> addWithMerge pc cond + Stopwatch.runMeasuringTime "PC_add_constraint" (fun () -> + match cond with + | True -> pc + | False -> falsePC + | _ when isFalse pc -> falsePC + | _ when pc |> toSeq |> Seq.contains !!cond -> falsePC + | _ -> addWithMerge pc cond + ) let public mapPC mapper (pc : pathCondition) : pathCondition = let mapAndAdd acc cond k = @@ -98,25 +100,27 @@ module internal PC = /// one path condition are independent with constants contained in another one /// let public fragments pc = - let groupConditionsByUnionFindParent groups cond constant = - let parent = constant |> Option.bind (fun constant -> PersistentUnionFind.tryFind constant pc.constants) - let updatedGroup = - PersistentDict.tryFind groups parent - |> function - | Some(condSet) -> PersistentSet.add condSet cond - | None -> PersistentSet.add PersistentSet.empty cond - PersistentDict.add parent updatedGroup groups - - let conditionsGroupToPathCondition (parent, conds) = - let fragmentConstants = - match parent with - | Some(parent) -> PersistentUnionFind.subset parent pc.constants - | None -> PersistentUnionFind.empty - let fragmentConditionsWithConstants = - PersistentSet.fold (fun dict cond -> PersistentDict.add cond parent dict) PersistentDict.empty conds - {constants = fragmentConstants; conditionsWithConstants = fragmentConditionsWithConstants} - - pc.conditionsWithConstants - |> PersistentDict.fold groupConditionsByUnionFindParent PersistentDict.empty - |> PersistentDict.toSeq - |> Seq.map conditionsGroupToPathCondition + Stopwatch.runMeasuringTime "PC_get_fragments" (fun () -> + let groupConditionsByUnionFindParent groups cond constant = + let parent = constant |> Option.bind (fun constant -> PersistentUnionFind.tryFind constant pc.constants) + let updatedGroup = + PersistentDict.tryFind groups parent + |> function + | Some(condSet) -> PersistentSet.add condSet cond + | None -> PersistentSet.add PersistentSet.empty cond + PersistentDict.add parent updatedGroup groups + + let conditionsGroupToPathCondition (parent, conds) = + let fragmentConstants = + match parent with + | Some(parent) -> PersistentUnionFind.subset parent pc.constants + | None -> PersistentUnionFind.empty + let fragmentConditionsWithConstants = + PersistentSet.fold (fun dict cond -> PersistentDict.add cond parent dict) PersistentDict.empty conds + {constants = fragmentConstants; conditionsWithConstants = fragmentConditionsWithConstants} + + pc.conditionsWithConstants + |> PersistentDict.fold groupConditionsByUnionFindParent PersistentDict.empty + |> PersistentDict.toSeq + |> Seq.map conditionsGroupToPathCondition + ) diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index d75d48311..a31bb68d0 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -797,7 +797,9 @@ module internal Z3 = | Status.SATISFIABLE -> let z3Model = optCtx.Model let updatedModel = {q.currentModel with state = {q.currentModel.state with model = q.currentModel.state.model}} - builder.UpdateModel z3Model updatedModel + Stopwatch.runMeasuringTime "update_model_after_check" (fun () -> + builder.UpdateModel z3Model updatedModel + ) // let usedPaths = // pathAtoms // |> Seq.filter (fun atom -> z3Model.Eval(atom, false).IsTrue) From 66e517c759a58a75292b02e6a59f0373aaed6ac6 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Mon, 28 Feb 2022 03:18:17 +0300 Subject: [PATCH 32/88] Measure union-find operations time --- VSharp.Utils/PersistentUnionFind.fs | 80 ++++++++++++++++------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs index 289c111dc..585a97123 100644 --- a/VSharp.Utils/PersistentUnionFind.fs +++ b/VSharp.Utils/PersistentUnionFind.fs @@ -31,13 +31,16 @@ module public PersistentUnionFind = /// /// Element not found let rec public find a puf = - try - PersistentDict.find puf.elements a - |> function - | Tail _ -> a - | Node(next) -> find next puf - with - _ -> raise (InvalidOperationException "Element not found") + Stopwatch.runMeasuringTime "puf_find" (fun () -> + try + PersistentDict.find puf.elements a + |> function + | Tail _ -> a + | Node(next) -> find next puf + with + _ -> raise (InvalidOperationException "Element not found") + ) + /// /// Returns representative element of the set containing the given element or @@ -57,43 +60,50 @@ module public PersistentUnionFind = /// Unions two sets containing the given elements /// let public union a b puf = - let aParentOption = tryFind a puf - let bParentOption = tryFind b puf - match aParentOption, bParentOption with - | Some(aParent), Some(bParent) when aParent <> bParent -> - let aTail = PersistentDict.find puf.elements aParent |> unwrapNode - let bTail = PersistentDict.find puf.elements bParent |> unwrapNode - let mergedElements = - puf.elements - |> PersistentDict.add aParent (Tail(bTail)) - |> PersistentDict.add bParent (Node(aTail)) - {elements = mergedElements} - | _ -> puf + Stopwatch.runMeasuringTime "puf_union" (fun () -> + let aParentOption = tryFind a puf + let bParentOption = tryFind b puf + match aParentOption, bParentOption with + | Some(aParent), Some(bParent) when aParent <> bParent -> + let aTail = PersistentDict.find puf.elements aParent |> unwrapNode + let bTail = PersistentDict.find puf.elements bParent |> unwrapNode + let mergedElements = + puf.elements + |> PersistentDict.add aParent (Tail(bTail)) + |> PersistentDict.add bParent (Node(aTail)) + {elements = mergedElements} + | _ -> puf + ) + /// /// Adds a single-element set containing the given element. If the element already exists, /// does nothing /// let public add puf a = - match tryFind a puf with - | Some _ -> puf - | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} + Stopwatch.runMeasuringTime "puf_add" (fun () -> + match tryFind a puf with + | Some _ -> puf + | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} + ) /// /// Returns a single-set union-find with the set containing the given element /// /// Element not found let public subset a puf = - let rec traverse current acc = - let next = - try - PersistentDict.find puf.elements current - with - _ -> raise (InvalidOperationException "Element not found") - let updatedDict = PersistentDict.add current next acc - let unwrappedNext = unwrapNode next - if (unwrappedNext <> a) then - traverse unwrappedNext updatedDict - else updatedDict - let subsetElements = traverse a PersistentDict.empty - {elements = subsetElements} + Stopwatch.runMeasuringTime "puf_subset" (fun () -> + let rec traverse current acc = + let next = + try + PersistentDict.find puf.elements current + with + _ -> raise (InvalidOperationException "Element not found") + let updatedDict = PersistentDict.add current next acc + let unwrappedNext = unwrapNode next + if (unwrappedNext <> a) then + traverse unwrappedNext updatedDict + else updatedDict + let subsetElements = traverse a PersistentDict.empty + {elements = subsetElements} + ) From 36a600ac8a37712f3689667719ba928b6c1a4d4e Mon Sep 17 00:00:00 2001 From: mxprshn Date: Mon, 28 Feb 2022 03:21:54 +0300 Subject: [PATCH 33/88] Add new mutable PC class --- VSharp.SILI.Core/NewPathCondition.fs | 79 ++++++++++++++++++++++++ VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 1 + 2 files changed, 80 insertions(+) create mode 100644 VSharp.SILI.Core/NewPathCondition.fs diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/NewPathCondition.fs new file mode 100644 index 000000000..19648e455 --- /dev/null +++ b/VSharp.SILI.Core/NewPathCondition.fs @@ -0,0 +1,79 @@ +namespace VSharp.Core +open VSharp +open VSharp.Utils +open System.Collections.Generic + +module internal PC2 = + + type private node = + | Tail of term * term pset + | Node of term + | Empty + + type PathCondition() = + + let constants = Dictionary() + let constraints = HashSet() + let mutable isTrivialFalse = false + + let rec findPrevious term = + let mutable found = Empty + if constants.TryGetValue(term, &found) + then + match found with + | Tail(representative, constraints) -> Some(term) + | Node nextTerm -> findPrevious nextTerm + | Empty -> __unreachable__() + else + None + + let rec find term = + let mutable found = Empty + if constants.TryGetValue(term, &found) + then + match found with + | Tail(representative, constraints) -> Some(representative, constraints) + | Node nextTerm -> find nextTerm + | Empty -> __unreachable__() + else + None + + let union oneConstant anotherConstant = + + + let becomeTrivialFalse() = + constants.Clear() + constraints.Clear() + isTrivialFalse <- true + + let addSubset constantsToAdd constraintsToAdd = + let firstConstant = constantsToAdd |> Seq.head + constantsToAdd + |> Seq.pairwise + |> Seq.iteri (fun i (previous, next) -> + if (i <> Seq.length constantsToAdd - 2) then + constants.[previous] <- Node(next) + else + constants.[previous] <- Tail(next, constraintsToAdd) + constants.[next] <- Node(firstConstant) + ) + + let addNewConstraintWithMerge newConstraint = + let constraintConstants = discoverConstants [newConstraint] + let newConstants = constraintConstants |> Seq.filter (fun c -> constants.ContainsKey(c) |> not) + + addSubset newConstants (PersistentSet.add PersistentSet.empty newConstraint) + constraints.Add(newConstraint) + + + member this.IsTrivialFalse = isTrivialFalse + + member this.Add newConstraint = + match newConstraint with + | True -> () + | False -> becomeTrivialFalse() + | _ when isTrivialFalse -> () + | _ when constraints.Contains(newConstraint) -> () + | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() + | _ -> addWithMerge pc cond + \ No newline at end of file diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index fd73214bb..c9435ad5b 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -33,6 +33,7 @@ + From a709a2526e768b16ffbdb6939b3a6734e7e80bc3 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sat, 5 Mar 2022 23:24:09 +0300 Subject: [PATCH 34/88] [feat] Implement all members of mutable PC --- VSharp.SILI.Core/NewPathCondition.fs | 142 +++++++++++++++++++++------ VSharp.Utils/Collections.fs | 9 ++ 2 files changed, 122 insertions(+), 29 deletions(-) diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/NewPathCondition.fs index 19648e455..10fffcdf3 100644 --- a/VSharp.SILI.Core/NewPathCondition.fs +++ b/VSharp.SILI.Core/NewPathCondition.fs @@ -9,44 +9,72 @@ module internal PC2 = | Tail of term * term pset | Node of term | Empty + + let private unwrapNode = + function + | Tail(term, _) -> term + | Node(term) -> term + | Empty -> invalidOp "Cannot unwrap empty node" - type PathCondition() = - - let constants = Dictionary() - let constraints = HashSet() - let mutable isTrivialFalse = false + type PathCondition private( + constants : Dictionary, + constraints : HashSet, + isTrivialFalse : bool + ) = + let mutable isTrivialFalse = isTrivialFalse - let rec findPrevious term = + let nextNode term = let mutable found = Empty - if constants.TryGetValue(term, &found) - then - match found with - | Tail(representative, constraints) -> Some(term) - | Node nextTerm -> findPrevious nextTerm - | Empty -> __unreachable__() - else - None + constants.TryGetValue(term, &found) |> ignore + found + + let rec findPrevious term = + match nextNode term with + | Tail(_, _) -> Some(term) + | Node nextTerm -> findPrevious nextTerm + | Empty -> None let rec find term = - let mutable found = Empty - if constants.TryGetValue(term, &found) - then - match found with - | Tail(representative, constraints) -> Some(representative, constraints) - | Node nextTerm -> find nextTerm - | Empty -> __unreachable__() - else - None + match nextNode term with + | Tail(representative, constraints) -> Some(representative, constraints) + | Node nextTerm -> find nextTerm + | Empty -> None let union oneConstant anotherConstant = - + match (findPrevious oneConstant), (findPrevious anotherConstant) with + | Some(onePrevious), Some(anotherPrevious) -> + match (nextNode onePrevious), (nextNode anotherPrevious) with + | Tail(oneRepresentative, oneConstraints), Tail(anotherRepresentative, anotherConstraints) when + oneRepresentative <> anotherRepresentative -> + let constraintsUnion = PersistentSet.union oneConstraints anotherConstraints + constants.[onePrevious] <- Tail(anotherRepresentative, constraintsUnion) + constants.[anotherPrevious] <- Node(oneRepresentative) + | _ -> __unreachable__() + | _ -> invalidOp "Constant not found in dictionary" + + let subset constantInSubset = + seq { + let rec inner currentConstant = + seq { + if currentConstant <> constantInSubset then + yield currentConstant + match nextNode currentConstant with + | Empty -> __unreachable__() + | node -> yield! inner (unwrapNode node) + } + match nextNode constantInSubset with + | Empty -> invalidOp "Constant not found in dictionary" + | node -> + yield constantInSubset + yield! inner (unwrapNode node) + } let becomeTrivialFalse() = constants.Clear() constraints.Clear() isTrivialFalse <- true - let addSubset constantsToAdd constraintsToAdd = + let addSubset (constants : Dictionary) constantsToAdd constraintsToAdd = let firstConstant = constantsToAdd |> Seq.head constantsToAdd |> Seq.pairwise @@ -58,22 +86,78 @@ module internal PC2 = constants.[next] <- Node(firstConstant) ) + let constSourcesIndependent = + function + | ConstantT(_, oneSrc, _), ConstantT(_, anotherSrc, _) -> oneSrc.IndependentWith anotherSrc + | _ -> true + let addNewConstraintWithMerge newConstraint = let constraintConstants = discoverConstants [newConstraint] - let newConstants = constraintConstants |> Seq.filter (fun c -> constants.ContainsKey(c) |> not) + let (oldConstants, newConstants) = + constraintConstants + |> Seq.splitBy (fun c -> constants.ContainsKey(c)) + + addSubset constants newConstants (PersistentSet.add PersistentSet.empty newConstraint) + constraints.Add(newConstraint) |> ignore - addSubset newConstants (PersistentSet.add PersistentSet.empty newConstraint) - constraints.Add(newConstraint) + Seq.allPairs newConstants constants.Keys + |> Seq.filter (constSourcesIndependent >> not) + |> Seq.iter (fun (oneConst, anotherConst) -> union oneConst anotherConst) + match (Seq.tryHead oldConstants) with + | Some(someOldConstant) -> + Seq.iter (fun constant -> union someOldConstant constant) oldConstants + match (Seq.tryHead newConstants) with + | Some(someNewConstant) -> union someNewConstant someOldConstant + | _ -> () + | _ -> () + + let copy() = + PathCondition(Dictionary(constants), HashSet(constraints), isTrivialFalse) + + new() = + PathCondition(Dictionary(), HashSet(), false) + + override this.ToString() = + Seq.map toString constraints |> Seq.sort |> join " /\ " member this.IsTrivialFalse = isTrivialFalse + member this.IsEmpty = constraints.Count = 0 + + member this.ToSeq() = seq constraints + member this.Add newConstraint = match newConstraint with | True -> () | False -> becomeTrivialFalse() | _ when isTrivialFalse -> () | _ when constraints.Contains(newConstraint) -> () + // what if constraint is not equal to newConstraint structurally, but is equal logically? | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() - | _ -> addWithMerge pc cond + | _ -> addNewConstraintWithMerge newConstraint + + member this.Map mapper = + let mapped = PathCondition() + Seq.iter (mapper >> mapped.Add) constraints + mapped + + member this.UnionWith (anotherPc : PathCondition) = + let union = copy() + anotherPc.ToSeq() |> Seq.iter union.Add + union + + member this.Fragments = + if isTrivialFalse then + Seq.singleton this + else + let getSubsetByRepresentative = + function + | Tail(representative, constraints) -> + let constants = Dictionary() + addSubset constants (subset representative) constraints + let constraints = HashSet(PersistentSet.toSeq constraints) + Some(PathCondition(constants, constraints, false)) + | _ -> None + Seq.choose getSubsetByRepresentative constants.Values \ No newline at end of file diff --git a/VSharp.Utils/Collections.fs b/VSharp.Utils/Collections.fs index 62211a370..f4ad3709b 100644 --- a/VSharp.Utils/Collections.fs +++ b/VSharp.Utils/Collections.fs @@ -31,6 +31,15 @@ module public Seq = lenProd <- lenProd * lengths.[i] Array.mapFold detachOne (idx, lenProd) (Array.init lengths.Length id) |> fst + let splitBy condition seq = + let grouped = Seq.groupBy (fun element -> condition element) seq + match (Seq.tryFind (fun (value, _) -> value) grouped), + (Seq.tryFind (fun (value, _) -> value |> not) grouped) with + | Some(_, trueSeq), Some(_, falseSeq) -> trueSeq, falseSeq + | Some(_, trueSeq), None -> trueSeq, Seq.empty + | None, Some(_, falseSeq) -> Seq.empty, falseSeq + | None, None -> Seq.empty, Seq.empty + module public List = let rec private mappedPartitionAcc f left right = function | [] -> (List.rev left, List.rev right) From 70abd261c2f30850a75f795212569bbf9050ee33 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sun, 6 Mar 2022 00:29:58 +0300 Subject: [PATCH 35/88] [feat] Replace old PC with new --- VSharp.SILI.Core/API.fs | 15 +-- VSharp.SILI.Core/API.fsi | 10 +- VSharp.SILI.Core/Copying.fs | 6 +- VSharp.SILI.Core/Memory.fs | 45 ++++---- VSharp.SILI.Core/Merging.fs | 7 +- VSharp.SILI.Core/NewPathCondition.fs | 19 ++-- VSharp.SILI.Core/PathCondition.fs | 126 ----------------------- VSharp.SILI.Core/SolverInteraction.fs | 2 +- VSharp.SILI.Core/State.fs | 4 +- VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 1 - VSharp.SILI/SILI.fs | 2 +- VSharp.SILI/Statistics.fs | 2 +- VSharp.SILI/TestGenerator.fs | 2 +- 13 files changed, 65 insertions(+), 176 deletions(-) delete mode 100644 VSharp.SILI.Core/PathCondition.fs diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 890c02c6a..106e455dc 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -131,10 +131,13 @@ module API = | _ -> internalfailf "Unboxing: expected heap reference, but got %O" reference let AddConstraint conditionState condition = Memory.addConstraint conditionState condition - let IsFalsePathCondition conditionState = PC.isFalse conditionState.pc - let Contradicts state condition = PC.add state.pc condition |> PC.isFalse - let PathConditionToSeq (pc : pathCondition) = PC.toSeq pc - let EmptyPathCondition = PC.empty + let IsFalsePathCondition conditionState = conditionState.pc.IsTrivialFalse + let Contradicts state condition = + let copy = state.pc.Copy() + copy.Add condition + copy.IsTrivialFalse + let PathConditionToSeq (pc : PC2.PathCondition) = pc.ToSeq() + let EmptyPathCondition() = PC2.PathCondition() module Types = let Numeric t = Types.Numeric t @@ -470,7 +473,7 @@ module API = | _ -> internalfailf "constructing string from char array: expected string reference, but got %O" dstRef let ComposeStates state state' = Memory.composeStates state state' - let WLP state pc' = PC.mapPC (Memory.fillHoles state) pc' |> PC.union state.pc + let WLP state (pc' : PC2.PathCondition) = (pc'.Map (Memory.fillHoles state)).UnionWith state.pc let Merge2States (s1 : state) (s2 : state) = Memory.merge2States s1 s2 let Merge2Results (r1, s1 : state) (r2, s2 : state) = Memory.merge2Results (r1, s1) (r2, s2) @@ -498,4 +501,4 @@ module API = module Print = let Dump state = Memory.dump state - let PrintPC pc = PC.toString pc + let PrintPC (pc : PC2.PathCondition) = pc.ToString() diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 42e2e61de..48cd2819d 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -18,7 +18,7 @@ module API = val StatedConditionalExecutionAppendResults : (state -> (state -> (term * state -> 'a) -> 'b) -> (state -> (state list -> 'a) -> 'a) -> (state -> (state list -> 'a) -> 'a) -> (state list -> 'a) -> 'b) val GuardedApplyExpression : term -> (term -> term) -> term - val GuardedApplyExpressionWithPC : pathCondition -> term -> (term -> term) -> term + val GuardedApplyExpressionWithPC : PC2.PathCondition -> term -> (term -> term) -> term val GuardedStatedApplyStatementK : state -> term -> (state -> term -> (term * state -> 'a) -> 'a) -> ((term * state) list -> 'a) -> 'a val GuardedStatedApplyk : (state -> term -> ('item -> 'a) -> 'a) -> state -> term -> ('item list -> 'item list) -> ('item list -> 'a) -> 'a @@ -94,8 +94,8 @@ module API = val AddConstraint : state -> term -> unit val IsFalsePathCondition : state -> bool val Contradicts : state -> term -> bool - val PathConditionToSeq : pathCondition -> term seq - val EmptyPathCondition : pathCondition + val PathConditionToSeq : PC2.PathCondition -> term seq + val EmptyPathCondition : unit -> PC2.PathCondition module Types = val Numeric : System.Type -> symbolicType @@ -274,7 +274,7 @@ module API = // TODO: get rid of all unnecessary stuff below! val ComposeStates : state -> state -> state list - val WLP : state -> pathCondition -> pathCondition + val WLP : state -> PC2.PathCondition -> PC2.PathCondition val Merge2States : state -> state -> state list val Merge2Results : term * state -> term * state -> (term * state) list @@ -286,7 +286,7 @@ module API = module Print = val Dump : state -> string - val PrintPC : pathCondition -> string + val PrintPC : PC2.PathCondition -> string // module Marshalling = // val Unmarshal : state -> obj -> term * state diff --git a/VSharp.SILI.Core/Copying.fs b/VSharp.SILI.Core/Copying.fs index 2bd14f928..575a6f3a0 100644 --- a/VSharp.SILI.Core/Copying.fs +++ b/VSharp.SILI.Core/Copying.fs @@ -24,7 +24,11 @@ module internal Copying = let constant = Constant "i" source Types.Int32 let leftBound = simplifyLessOrEqual lowerBound constant id let rightBound = simplifyLessOrEqual constant upperBound id - let pcWithBounds = PC.add (PC.add state.pc leftBound) rightBound + let pcWithBounds = + let copy = state.pc.Copy() + copy.Add leftBound + copy.Add rightBound + copy state.pc <- pcWithBounds constant diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index b500f3a3b..7d1776369 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -36,7 +36,7 @@ module internal Memory = x = VectorTime.zero let addConstraint (s : state) cond = - s.pc <- PC.add s.pc cond + s.pc.Add cond let delinearizeArrayIndex ind lens lbs = let detachOne (acc, lens) lb = @@ -656,8 +656,9 @@ module internal Memory = match term.term with | Union gvs -> let filterUnsat (g, v) k = - let pc = PC.add state.pc g - if PC.isFalse pc then k None + let pc = state.pc.Copy() + pc.Add g + if pc.IsTrivialFalse then k None else Some (pc, v) |> k Cps.List.choosek filterUnsat gvs (fun pcs -> match pcs with @@ -677,9 +678,9 @@ module internal Memory = let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = // Returns PC containing only constraints dependent with cond - let keepDependentWith pc cond = - PC.fragments pc - |> Seq.tryFind (PC.toSeq >> Seq.contains cond) + let keepDependentWith (pc : PC2.PathCondition) cond = + pc.Fragments + |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) |> Option.defaultValue pc let execution thenState elseState condition k = assert (condition <> True && condition <> False) @@ -688,15 +689,15 @@ module internal Memory = merge2Results thenResult elseResult |> k)) conditionInvocation state (fun (condition, conditionState) -> let negatedCondition = !!condition - let thenPc = PC.add state.pc condition - let elsePc = PC.add state.pc negatedCondition + let thenPc = PC2.add state.pc condition + let elsePc = PC2.add state.pc negatedCondition let independentThenPc = keepDependentWith thenPc condition // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond let independentElsePc = keepDependentWith elsePc negatedCondition - if PC.isFalse independentThenPc then + if independentThenPc.IsTrivialFalse then conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) - elif PC.isFalse independentElsePc then + elif independentElsePc.IsTrivialFalse then conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else @@ -712,11 +713,11 @@ module internal Memory = conditionState.pc <- elsePc conditionState.model <- Some model.mdl StatedLogger.log conditionState.id $"Model stack: %s{model.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> conditionState.pc <- elsePc - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat thenModel -> conditionState.pc <- independentElsePc @@ -726,7 +727,7 @@ module internal Memory = conditionState.pc <- thenPc conditionState.model <- Some thenModel.mdl StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> conditionState.pc <- thenPc @@ -735,8 +736,8 @@ module internal Memory = StatedLogger.copy thenState.id elseState.id StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" - StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" elseState.model <- Some elseModel.mdl thenState.model <- Some thenModel.mdl execution thenState elseState condition k) @@ -832,7 +833,7 @@ module internal Memory = let classSize = TypeUtils.internalSizeOf t |> int |> makeNumber let failCondition = simplifyGreater (add offset viewSize) classSize id ||| simplifyLess offset (makeNumber 0) id // NOTE: disables overflow in solver - state.pc <- PC.add state.pc (makeExpressionNoOvf failCondition id) // TODO: think more about overflow + state.pc <- PC2.add state.pc (makeExpressionNoOvf failCondition id) // TODO: think more about overflow checkUnsafeRead state reportError failCondition // TODO: Add undefined behaviour: @@ -853,7 +854,7 @@ module internal Memory = let arraySize = List.fold mul elementSize lens let failCondition = simplifyGreater (Option.get size |> makeNumber |> add offset) arraySize id ||| simplifyLess offset (makeNumber 0) id // NOTE: disables overflow in solver - state.pc <- PC.add state.pc (makeExpressionNoOvf failCondition id) + state.pc <- PC2.add state.pc (makeExpressionNoOvf failCondition id) checkUnsafeRead state reportError failCondition let firstElement = div offset elementSize let elementOffset = rem offset elementSize @@ -916,7 +917,7 @@ module internal Memory = let locSize = term |> typeOf |> sizeOf |> int |> makeNumber let viewSize = nonVoidType |> Option.get |> sizeOf |> int |> makeNumber let failCondition = simplifyGreater (add offset viewSize) locSize id &&& simplifyLess offset (makeNumber 0) id - state.pc <- PC.add state.pc (makeExpressionNoOvf failCondition id) // TODO: think more about overflow + state.pc <- PC2.add state.pc (makeExpressionNoOvf failCondition id) // TODO: think more about overflow checkUnsafeRead state reportError failCondition let endByte = Option.map (sizeOf >> makeNumber >> add offset) nonVoidType readTermUnsafe term offset endByte @@ -1168,7 +1169,7 @@ module internal Memory = let locSize = term |> typeOf |> sizeOf |> int |> makeNumber let viewSize = value |> Terms.sizeOf |> int |> makeNumber let failCondition = simplifyGreater (add offset viewSize) locSize id &&& simplifyLess offset (makeNumber 0) id - state.pc <- PC.add state.pc (makeExpressionNoOvf failCondition id) // TODO: think more about overflow + state.pc <- PC2.add state.pc (makeExpressionNoOvf failCondition id) // TODO: think more about overflow checkUnsafeRead state reportError failCondition let updatedTerm = writeTermUnsafe term offset value writeStackLocation state loc updatedTerm @@ -1511,7 +1512,7 @@ module internal Memory = assert(not <| VectorTime.isEmpty state.currentTime) // TODO: do nothing if state is empty! list { - let pc = PC.mapPC (fillHoles state) state'.pc |> PC.union state.pc + let pc = (state'.pc.Map (fillHoles state)).UnionWith state.pc // Note: this is not final evaluationStack of resulting cilState, here we forget left state's opStack at all let evaluationStack = composeEvaluationStacksOf state state'.evaluationStack let exceptionRegister = composeRaisedExceptionsOf state state'.exceptionsRegister @@ -1534,7 +1535,7 @@ module internal Memory = if not <| isFalse g then return { id = Guid.NewGuid().ToString() - pc = if isTrue g then pc else PC.add pc g + pc = if isTrue g then pc else PC2.add pc g evaluationStack = evaluationStack exceptionsRegister = exceptionRegister stack = stack @@ -1607,7 +1608,7 @@ module internal Memory = // TODO: print lower bounds? let sortBy sorter = Seq.sortBy (fst >> sorter) let sb = StringBuilder() - let sb = if PC.isEmpty s.pc then sb else s.pc |> PC.toString |> sprintf ("Path condition: %s") |> PrettyPrinting.appendLine sb + let sb = if s.pc.IsEmpty then sb else s.pc.ToString() |> sprintf ("Path condition: %s") |> PrettyPrinting.appendLine sb let sb = dumpDict "Fields" (sortBy toString) toString (MemoryRegion.toString " ") sb s.classFields let sb = dumpDict "Concrete memory" sortVectorTime VectorTime.print toString sb (s.concreteMemory |> Seq.map (fun kvp -> (kvp.Key, kvp.Value)) |> PersistentDict.ofSeq) let sb = dumpDict "Array contents" (sortBy arrayTypeToString) arrayTypeToString (MemoryRegion.toString " ") sb s.arrays diff --git a/VSharp.SILI.Core/Merging.fs b/VSharp.SILI.Core/Merging.fs index 7f10d5707..656b72688 100644 --- a/VSharp.SILI.Core/Merging.fs +++ b/VSharp.SILI.Core/Merging.fs @@ -102,10 +102,11 @@ module internal Merging = let guardedApplyk f term k = commonGuardedApplyk f term merge k let guardedApply f term = guardedApplyk (Cps.ret f) term id - let commonGuardedMapkWithPC pc mapper gvs merge k = + let commonGuardedMapkWithPC (pc : PC2.PathCondition) mapper gvs merge k = let foldFunc gvs (g, v) k = - let pc' = PC.add pc g - if PC.isFalse pc' then k gvs + let pc' = pc.Copy() + pc'.Add(g) + if pc'.IsTrivialFalse then k gvs else mapper v (fun t -> k ((g, t) :: gvs)) Cps.List.foldlk foldFunc [] gvs (merge >> k) diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/NewPathCondition.fs index 10fffcdf3..e3e55d786 100644 --- a/VSharp.SILI.Core/NewPathCondition.fs +++ b/VSharp.SILI.Core/NewPathCondition.fs @@ -3,7 +3,7 @@ open VSharp open VSharp.Utils open System.Collections.Generic -module internal PC2 = +module public PC2 = type private node = | Tail of term * term pset @@ -16,7 +16,7 @@ module internal PC2 = | Node(term) -> term | Empty -> invalidOp "Cannot unwrap empty node" - type PathCondition private( + type public PathCondition private( constants : Dictionary, constraints : HashSet, isTrivialFalse : bool @@ -112,15 +112,15 @@ module internal PC2 = | _ -> () | _ -> () - let copy() = - PathCondition(Dictionary(constants), HashSet(constraints), isTrivialFalse) - new() = PathCondition(Dictionary(), HashSet(), false) override this.ToString() = Seq.map toString constraints |> Seq.sort |> join " /\ " + member this.Copy() = + PathCondition(Dictionary(constants), HashSet(constraints), isTrivialFalse) + member this.IsTrivialFalse = isTrivialFalse member this.IsEmpty = constraints.Count = 0 @@ -143,7 +143,7 @@ module internal PC2 = mapped member this.UnionWith (anotherPc : PathCondition) = - let union = copy() + let union = this.Copy() anotherPc.ToSeq() |> Seq.iter union.Add union @@ -160,4 +160,11 @@ module internal PC2 = Some(PathCondition(constants, constraints, false)) | _ -> None Seq.choose getSubsetByRepresentative constants.Values + + let public add (pc : PathCondition) newConstraint = + let copy = pc.Copy() + copy.Add newConstraint + copy + + let public toSeq (pc : PathCondition) = pc.ToSeq() \ No newline at end of file diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs deleted file mode 100644 index 16be7fb62..000000000 --- a/VSharp.SILI.Core/PathCondition.fs +++ /dev/null @@ -1,126 +0,0 @@ -namespace VSharp.Core -open VSharp -open VSharp.Utils - -(* - Path condition is represented as a union-find of sets disjoint by independence of - constants in them and a dictionary where a condition is mapped to some constant - contained in it or None if the condition doesn't contain any constants -*) -type pathCondition = - private {constants : pUnionFind; conditionsWithConstants : pdict} - -// Invariants: -// - PC does not contain True -// - if PC contains False then False is the only element in PC - -module internal PC = - - let public empty = - {constants = PersistentUnionFind.empty - conditionsWithConstants = PersistentDict.empty} - - let public isEmpty pc = PersistentDict.isEmpty pc.conditionsWithConstants - - let public toSeq pc = PersistentDict.keys pc.conditionsWithConstants - - let private falsePC = - {constants = PersistentUnionFind.empty - conditionsWithConstants = PersistentDict.add False None PersistentDict.empty} - - let public isFalse pc = - let isFalsePC = pc |> toSeq |> Seq.contains False - if isFalsePC then assert(toSeq pc |> Seq.length = 1) - isFalsePC - - let private someSetElement = PersistentSet.toSeq >> Seq.tryHead - - let private constSourcesIndependent = - function - | ConstantT(_, oneSrc, _), ConstantT(_, anotherSrc, _) -> oneSrc.IndependentWith anotherSrc - | _ -> true - - let private addWithMerge pc cond : pathCondition = - let condConsts = discoverConstants [cond] |> PersistentSet.ofSeq - - let pufWithNewConsts = - condConsts - |> PersistentSet.filter (fun t -> None = PersistentUnionFind.tryFind t pc.constants) - |> PersistentSet.fold PersistentUnionFind.add pc.constants - - // Merge sets of constants dependent in terms of ISymbolicConstantSource - let pufMergedByConstantSource = - Seq.allPairs - (condConsts |> PersistentSet.toSeq) - (pc.constants |> PersistentUnionFind.toSeq) - |> Seq.filter (constSourcesIndependent >> not) - |> Seq.fold (fun puf (const1, const2) -> PersistentUnionFind.union const1 const2 puf) pufWithNewConsts - - (* - Merge sets of constants dependent in terms of reachability in graph where - edge between constants means that they are contained in the same condition - *) - let pufMergedByDependentCondition = - condConsts - |> someSetElement - |> function - | Some(parent) -> - PersistentSet.fold (fun puf t -> PersistentUnionFind.union t parent puf) pufMergedByConstantSource condConsts - | None -> pufMergedByConstantSource - - {constants = pufMergedByDependentCondition - conditionsWithConstants = - condConsts - |> someSetElement - |> (fun head -> PersistentDict.add cond head pc.conditionsWithConstants)} - - let public add pc cond : pathCondition = - Stopwatch.runMeasuringTime "PC_add_constraint" (fun () -> - match cond with - | True -> pc - | False -> falsePC - | _ when isFalse pc -> falsePC - | _ when pc |> toSeq |> Seq.contains !!cond -> falsePC - | _ -> addWithMerge pc cond - ) - - let public mapPC mapper (pc : pathCondition) : pathCondition = - let mapAndAdd acc cond k = - let acc' = mapper cond |> add acc - if isFalse acc' then falsePC else k acc' - Cps.Seq.foldlk mapAndAdd empty (toSeq pc) id - let public mapSeq mapper (pc : pathCondition) = toSeq pc |> Seq.map mapper - - let toString pc = mapSeq toString pc |> Seq.sort |> join " /\ " - - let public union (pc1 : pathCondition) (pc2 : pathCondition) = Seq.fold add pc1 (toSeq pc2) - - /// - /// Returns the sequence of path conditions such that constants contained in - /// one path condition are independent with constants contained in another one - /// - let public fragments pc = - Stopwatch.runMeasuringTime "PC_get_fragments" (fun () -> - let groupConditionsByUnionFindParent groups cond constant = - let parent = constant |> Option.bind (fun constant -> PersistentUnionFind.tryFind constant pc.constants) - let updatedGroup = - PersistentDict.tryFind groups parent - |> function - | Some(condSet) -> PersistentSet.add condSet cond - | None -> PersistentSet.add PersistentSet.empty cond - PersistentDict.add parent updatedGroup groups - - let conditionsGroupToPathCondition (parent, conds) = - let fragmentConstants = - match parent with - | Some(parent) -> PersistentUnionFind.subset parent pc.constants - | None -> PersistentUnionFind.empty - let fragmentConditionsWithConstants = - PersistentSet.fold (fun dict cond -> PersistentDict.add cond parent dict) PersistentDict.empty conds - {constants = fragmentConstants; conditionsWithConstants = fragmentConditionsWithConstants} - - pc.conditionsWithConstants - |> PersistentDict.fold groupConditionsByUnionFindParent PersistentDict.empty - |> PersistentDict.toSeq - |> Seq.map conditionsGroupToPathCondition - ) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 726b955a1..037cda302 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -38,7 +38,7 @@ module public SolverInteraction = let checkSat state = let ctx = getEncodingContext state - let formula = PC.toSeq state.pc |> conjunction + let formula = state.pc.ToSeq() |> conjunction match solver with | Some s -> let model = diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index b18273cbf..559ee6209 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -117,7 +117,7 @@ and [] state = { id : string - mutable pc : pathCondition + mutable pc : PC2.PathCondition mutable evaluationStack : evaluationStack mutable stack : callStack // Arguments and local variables mutable stackBuffers : pdict // Buffers allocated via stackAlloc @@ -147,7 +147,7 @@ and module public State = let makeEmpty modelState = { id = Guid.NewGuid().ToString() - pc = PC.empty + pc = PC2.PathCondition() evaluationStack = EvaluationStack.empty exceptionsRegister = NoException stack = CallStack.empty diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index c9435ad5b..668c19af3 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -32,7 +32,6 @@ - diff --git a/VSharp.SILI/SILI.fs b/VSharp.SILI/SILI.fs index 029a78cc4..1ed7ef3eb 100644 --- a/VSharp.SILI/SILI.fs +++ b/VSharp.SILI/SILI.fs @@ -44,7 +44,7 @@ type public SILI(options : SiliOptions) = let coveragePobsForMethod (method : MethodBase) = let cfg = CFG.findCfg method cfg.sortedOffsets |> Seq.map (fun offset -> - {loc = {offset = offset; method = method}; lvl = infty; pc = EmptyPathCondition}) + {loc = {offset = offset; method = method}; lvl = infty; pc = EmptyPathCondition()}) |> List.ofSeq let reportState reporter isError method cmdArgs state = diff --git a/VSharp.SILI/Statistics.fs b/VSharp.SILI/Statistics.fs index 0bd38a92f..90e1634d0 100644 --- a/VSharp.SILI/Statistics.fs +++ b/VSharp.SILI/Statistics.fs @@ -12,7 +12,7 @@ open VSharp.Utils open CilStateOperations open ipOperations -type pob = {loc : codeLocation; lvl : uint; pc : pathCondition} +type pob = {loc : codeLocation; lvl : uint; pc : PC2.PathCondition} with override x.ToString() = sprintf "loc = %O; lvl = %d; pc = %s" x.loc x.lvl (Print.PrintPC x.pc) diff --git a/VSharp.SILI/TestGenerator.fs b/VSharp.SILI/TestGenerator.fs index c84335264..d25697ca3 100644 --- a/VSharp.SILI/TestGenerator.fs +++ b/VSharp.SILI/TestGenerator.fs @@ -199,7 +199,7 @@ module TestGenerator = match cilState.state.model with | Some model -> model2test test isError hasException indices m model cmdArgs cilState - | None when cilState.state.pc = EmptyPathCondition -> + | None when cilState.state.pc.IsEmpty -> // NOTE: case when no branches occured let emptyState = Memory.EmptyState None emptyState.allocatedTypes <- cilState.state.allocatedTypes From 173fcf86fd6e27bf9fa55219c6768ac2a0ee3f15 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Sun, 6 Mar 2022 15:07:42 +0300 Subject: [PATCH 36/88] [fix] Fix empty const sequence bug and enable coverage --- VSharp.SILI.Core/NewPathCondition.fs | 43 +++++++++++++++------------- VSharp.Test/IntegrationTests.cs | 6 ++-- VSharp.Utils/CoverageTool.fs | 12 +++----- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/NewPathCondition.fs index e3e55d786..81ab9d563 100644 --- a/VSharp.SILI.Core/NewPathCondition.fs +++ b/VSharp.SILI.Core/NewPathCondition.fs @@ -16,11 +16,8 @@ module public PC2 = | Node(term) -> term | Empty -> invalidOp "Cannot unwrap empty node" - type public PathCondition private( - constants : Dictionary, - constraints : HashSet, - isTrivialFalse : bool - ) = + type public PathCondition private(constants : Dictionary, constraints : HashSet, isTrivialFalse : bool) = + let mutable isTrivialFalse = isTrivialFalse let nextNode term = @@ -30,7 +27,7 @@ module public PC2 = let rec findPrevious term = match nextNode term with - | Tail(_, _) -> Some(term) + | Tail _ -> Some(term) | Node nextTerm -> findPrevious nextTerm | Empty -> None @@ -49,7 +46,7 @@ module public PC2 = let constraintsUnion = PersistentSet.union oneConstraints anotherConstraints constants.[onePrevious] <- Tail(anotherRepresentative, constraintsUnion) constants.[anotherPrevious] <- Node(oneRepresentative) - | _ -> __unreachable__() + | _ -> () | _ -> invalidOp "Constant not found in dictionary" let subset constantInSubset = @@ -75,16 +72,21 @@ module public PC2 = isTrivialFalse <- true let addSubset (constants : Dictionary) constantsToAdd constraintsToAdd = + if Seq.isEmpty constantsToAdd then + Logger.info "lolkek" let firstConstant = constantsToAdd |> Seq.head - constantsToAdd - |> Seq.pairwise - |> Seq.iteri (fun i (previous, next) -> - if (i <> Seq.length constantsToAdd - 2) then - constants.[previous] <- Node(next) - else - constants.[previous] <- Tail(next, constraintsToAdd) - constants.[next] <- Node(firstConstant) - ) + if Seq.length constantsToAdd = 1 then + constants.[firstConstant] <- Tail(firstConstant, constraintsToAdd) + else + constantsToAdd + |> Seq.pairwise + |> Seq.iteri (fun i (previous, next) -> + if (i <> Seq.length constantsToAdd - 2) then + constants.[previous] <- Node(next) + else + constants.[previous] <- Tail(next, constraintsToAdd) + constants.[next] <- Node(firstConstant) + ) let constSourcesIndependent = function @@ -93,11 +95,12 @@ module public PC2 = let addNewConstraintWithMerge newConstraint = let constraintConstants = discoverConstants [newConstraint] - let (oldConstants, newConstants) = + let oldConstants, newConstants = constraintConstants |> Seq.splitBy (fun c -> constants.ContainsKey(c)) - - addSubset constants newConstants (PersistentSet.add PersistentSet.empty newConstraint) + + // are there constraints without constants at all? + addSubset constants constraintConstants (PersistentSet.add PersistentSet.empty newConstraint) constraints.Add(newConstraint) |> ignore Seq.allPairs newConstants constants.Keys @@ -106,7 +109,7 @@ module public PC2 = match (Seq.tryHead oldConstants) with | Some(someOldConstant) -> - Seq.iter (fun constant -> union someOldConstant constant) oldConstants + Seq.iter (union someOldConstant) oldConstants match (Seq.tryHead newConstants) with | Some(someNewConstant) -> union someNewConstant someOldConstant | _ -> () diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 6da138599..724cd6a89 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -106,7 +106,7 @@ private TestResult Explore(TestExecutionContext context) var coverageTool = new CoverageTool(unitTests.TestDirectory.FullName, Directory.GetCurrentDirectory()); coverageTool.Run(unitTests.TestDirectory); - /*int coverage = coverageTool.GetCoverage(methodInfo); + int coverage = coverageTool.GetCoverage(methodInfo); if (coverage != _expectedCoverage) { context.CurrentResult.SetResult(ResultState.Failure, @@ -115,9 +115,7 @@ private TestResult Explore(TestExecutionContext context) else { context.CurrentResult.SetResult(ResultState.Success); - }*/ - - context.CurrentResult.SetResult(ResultState.Success); + } } else { diff --git a/VSharp.Utils/CoverageTool.fs b/VSharp.Utils/CoverageTool.fs index 0c606e9e0..0ce063745 100644 --- a/VSharp.Utils/CoverageTool.fs +++ b/VSharp.Utils/CoverageTool.fs @@ -26,7 +26,7 @@ type CoverageTool(testDir : string, runnerDir : string) = proc.WaitForExit() proc.ExitCode, output.ToString(), error.ToString() -(* let run = + let run = let _, localTools, _ = runDotnet "tool list" let globalArg = if localTools.Contains "dotcover" then "" @@ -37,11 +37,7 @@ type CoverageTool(testDir : string, runnerDir : string) = fun (directory : DirectoryInfo) -> let filters = ["-:module=Microsoft.*"; "-:module=FSharp.*"; "-:class=VSharp.*"; "-:module=VSharp.Utils"] let code, _, error = runDotnet <| sprintf "dotcover --dcFilters=\"%s\" %s%cVSharp.TestRunner.dll %s --dcReportType=DetailedXML %s" (filters |> join ";") runnerDir Path.DirectorySeparatorChar directory.FullName globalArg - code = 0, error*) - - let run (directory : DirectoryInfo) = - let code, _, error = runDotnet <| sprintf " %s%cVSharp.TestRunner.dll %s" runnerDir Path.DirectorySeparatorChar directory.FullName - code = 0, error + code = 0, error let mutable doc : XmlDocument = null @@ -78,8 +74,8 @@ type CoverageTool(testDir : string, runnerDir : string) = let success, error = run testDir if not success then raise <| InvalidOperationException ("Running dotCover failed: " + error) -(* doc <- XmlDocument() - doc.Load(sprintf "%s%cdotCover.Output.xml" testDir.FullName Path.DirectorySeparatorChar)*) + doc <- XmlDocument() + doc.Load(sprintf "%s%cdotCover.Output.xml" testDir.FullName Path.DirectorySeparatorChar) member x.GetCoverage (m : MethodInfo) = if String.IsNullOrEmpty m.DeclaringType.Namespace then From 4370685315b8800304993a61f66894b571b05a73 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 7 Mar 2022 15:43:48 +0300 Subject: [PATCH 37/88] [fix] Fix for the case when there are constraints without constants --- VSharp.SILI.Core/NewPathCondition.fs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/NewPathCondition.fs index 81ab9d563..d587f3da1 100644 --- a/VSharp.SILI.Core/NewPathCondition.fs +++ b/VSharp.SILI.Core/NewPathCondition.fs @@ -31,12 +31,6 @@ module public PC2 = | Node nextTerm -> findPrevious nextTerm | Empty -> None - let rec find term = - match nextNode term with - | Tail(representative, constraints) -> Some(representative, constraints) - | Node nextTerm -> find nextTerm - | Empty -> None - let union oneConstant anotherConstant = match (findPrevious oneConstant), (findPrevious anotherConstant) with | Some(onePrevious), Some(anotherPrevious) -> @@ -73,7 +67,7 @@ module public PC2 = let addSubset (constants : Dictionary) constantsToAdd constraintsToAdd = if Seq.isEmpty constantsToAdd then - Logger.info "lolkek" + Logger.info "kek" let firstConstant = constantsToAdd |> Seq.head if Seq.length constantsToAdd = 1 then constants.[firstConstant] <- Tail(firstConstant, constraintsToAdd) @@ -87,6 +81,16 @@ module public PC2 = constants.[previous] <- Tail(next, constraintsToAdd) constants.[next] <- Node(firstConstant) ) + + let addConstraintsToSubset subsetConstant constraintsToAdd = + match findPrevious subsetConstant with + | Some(previous) -> + match nextNode previous with + | Tail(representative, constraints) -> + let constraintsUnion = PersistentSet.union constraints constraintsToAdd + constants.[previous] <- Tail(representative, constraintsUnion) + | _ -> __unreachable__() + | _ -> __unreachable__() let constSourcesIndependent = function @@ -97,10 +101,16 @@ module public PC2 = let constraintConstants = discoverConstants [newConstraint] let oldConstants, newConstants = constraintConstants - |> Seq.splitBy (fun c -> constants.ContainsKey(c)) + |> Seq.splitBy constants.ContainsKey // are there constraints without constants at all? - addSubset constants constraintConstants (PersistentSet.add PersistentSet.empty newConstraint) + // answer: yes, in ArrayConcreteUnsafeRead, is it ok? + let newConstraintSet = PersistentSet.add PersistentSet.empty newConstraint + if Seq.isEmpty newConstants |> not then + addSubset constants newConstants newConstraintSet + else if Seq.isEmpty oldConstants |> not then + addConstraintsToSubset (Seq.head oldConstants) newConstraintSet + constraints.Add(newConstraint) |> ignore Seq.allPairs newConstants constants.Keys From 453775e9756fa1a0ed72c2aec362040993313c23 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 7 Mar 2022 15:57:22 +0300 Subject: [PATCH 38/88] [fix] Disable coverage again --- VSharp.Test/IntegrationTests.cs | 5 +++-- VSharp.Utils/CoverageTool.fs | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 724cd6a89..f05ab967d 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -106,7 +106,7 @@ private TestResult Explore(TestExecutionContext context) var coverageTool = new CoverageTool(unitTests.TestDirectory.FullName, Directory.GetCurrentDirectory()); coverageTool.Run(unitTests.TestDirectory); - int coverage = coverageTool.GetCoverage(methodInfo); +/* int coverage = coverageTool.GetCoverage(methodInfo); if (coverage != _expectedCoverage) { context.CurrentResult.SetResult(ResultState.Failure, @@ -115,7 +115,8 @@ private TestResult Explore(TestExecutionContext context) else { context.CurrentResult.SetResult(ResultState.Success); - } + }*/ + context.CurrentResult.SetResult(ResultState.Success); } else { diff --git a/VSharp.Utils/CoverageTool.fs b/VSharp.Utils/CoverageTool.fs index 0ce063745..2d0eccb17 100644 --- a/VSharp.Utils/CoverageTool.fs +++ b/VSharp.Utils/CoverageTool.fs @@ -26,7 +26,7 @@ type CoverageTool(testDir : string, runnerDir : string) = proc.WaitForExit() proc.ExitCode, output.ToString(), error.ToString() - let run = +(* let run = let _, localTools, _ = runDotnet "tool list" let globalArg = if localTools.Contains "dotcover" then "" @@ -37,7 +37,11 @@ type CoverageTool(testDir : string, runnerDir : string) = fun (directory : DirectoryInfo) -> let filters = ["-:module=Microsoft.*"; "-:module=FSharp.*"; "-:class=VSharp.*"; "-:module=VSharp.Utils"] let code, _, error = runDotnet <| sprintf "dotcover --dcFilters=\"%s\" %s%cVSharp.TestRunner.dll %s --dcReportType=DetailedXML %s" (filters |> join ";") runnerDir Path.DirectorySeparatorChar directory.FullName globalArg - code = 0, error + code = 0, error*) + + let run (directory : DirectoryInfo) = + let code, _, error = runDotnet <| sprintf " %s%cVSharp.TestRunner.dll %s" runnerDir Path.DirectorySeparatorChar directory.FullName + code = 0, error let mutable doc : XmlDocument = null @@ -74,8 +78,8 @@ type CoverageTool(testDir : string, runnerDir : string) = let success, error = run testDir if not success then raise <| InvalidOperationException ("Running dotCover failed: " + error) - doc <- XmlDocument() - doc.Load(sprintf "%s%cdotCover.Output.xml" testDir.FullName Path.DirectorySeparatorChar) +(* doc <- XmlDocument() + doc.Load(sprintf "%s%cdotCover.Output.xml" testDir.FullName Path.DirectorySeparatorChar)*) member x.GetCoverage (m : MethodInfo) = if String.IsNullOrEmpty m.DeclaringType.Namespace then From b251e88c7887cddb90484d5fac5ea4424675b3da Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 7 Mar 2022 17:47:25 +0300 Subject: [PATCH 39/88] [fix] Measure time of Add and Fragments --- VSharp.SILI.Core/NewPathCondition.fs | 50 ++++++++++++++++------------ 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/NewPathCondition.fs index d587f3da1..a3df4fa04 100644 --- a/VSharp.SILI.Core/NewPathCondition.fs +++ b/VSharp.SILI.Core/NewPathCondition.fs @@ -131,8 +131,10 @@ module public PC2 = override this.ToString() = Seq.map toString constraints |> Seq.sort |> join " /\ " - member this.Copy() = - PathCondition(Dictionary(constants), HashSet(constraints), isTrivialFalse) + member this.Copy() = + let inner() = + PathCondition(Dictionary(constants), HashSet(constraints), isTrivialFalse) + Stopwatch.runMeasuringTime "PC_Copy" inner member this.IsTrivialFalse = isTrivialFalse @@ -141,14 +143,16 @@ module public PC2 = member this.ToSeq() = seq constraints member this.Add newConstraint = - match newConstraint with - | True -> () - | False -> becomeTrivialFalse() - | _ when isTrivialFalse -> () - | _ when constraints.Contains(newConstraint) -> () - // what if constraint is not equal to newConstraint structurally, but is equal logically? - | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() - | _ -> addNewConstraintWithMerge newConstraint + let inner() = + match newConstraint with + | True -> () + | False -> becomeTrivialFalse() + | _ when isTrivialFalse -> () + | _ when constraints.Contains(newConstraint) -> () + // what if constraint is not equal to newConstraint structurally, but is equal logically? + | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() + | _ -> addNewConstraintWithMerge newConstraint + Stopwatch.runMeasuringTime "PC_Add" inner member this.Map mapper = let mapped = PathCondition() @@ -161,18 +165,20 @@ module public PC2 = union member this.Fragments = - if isTrivialFalse then - Seq.singleton this - else - let getSubsetByRepresentative = - function - | Tail(representative, constraints) -> - let constants = Dictionary() - addSubset constants (subset representative) constraints - let constraints = HashSet(PersistentSet.toSeq constraints) - Some(PathCondition(constants, constraints, false)) - | _ -> None - Seq.choose getSubsetByRepresentative constants.Values + let inner() = + if isTrivialFalse then + Seq.singleton this + else + let getSubsetByRepresentative = + function + | Tail(representative, constraints) -> + let constants = Dictionary() + addSubset constants (subset representative) constraints + let constraints = HashSet(PersistentSet.toSeq constraints) + Some(PathCondition(constants, constraints, false)) + | _ -> None + Seq.choose getSubsetByRepresentative constants.Values + Stopwatch.runMeasuringTime "PC_Fragments" inner let public add (pc : PathCondition) newConstraint = let copy = pc.Copy() From b325b539dd49a64bb213500a98ab04475bd8ab69 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Fri, 11 Mar 2022 19:48:55 +0300 Subject: [PATCH 40/88] [fix] Fix master merge conflicts with --- VSharp.SILI.Core/API.fs | 2 +- VSharp.SILI.Core/Memory.fs | 125 +++++++++++------------------------ VSharp.Solver/Z3.fs | 2 +- VSharp.Utils/StatedLogger.fs | 2 +- global.json | 5 -- 5 files changed, 43 insertions(+), 93 deletions(-) delete mode 100644 global.json diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 31b9142d2..94a473ab1 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -66,7 +66,7 @@ module API = | {term = ConcreteHeapAddress _} -> () | term -> internalfailf "Unexpected address %O in subtyping constraint!" term - PC.toSeq state.pc |> Seq.iter (term >> function + state.pc.ToSeq() |> Seq.iter (term >> function | Constant(_, TypeCasting.TypeSubtypeTypeSource _, _) -> __notImplemented__() | Constant(_, TypeCasting.RefSubtypeTypeSource(address, typ), _) -> add supertypeConstraints address typ | Constant(_, TypeCasting.TypeSubtypeRefSource(typ, address), _) -> add subtypeConstraints address typ diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 14d37c449..8a7522e3a 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -473,8 +473,8 @@ module internal Memory = match term.term with | Union gvs -> let filterUnsat (g, v) k = - let pc = PC.add state.pc g - if PC.isFalse pc then k None + let pc = PC2.add state.pc g + if pc.IsTrivialFalse then k None else Some (pc, v) |> k Cps.List.choosek filterUnsat gvs (fun pcs -> match pcs with @@ -491,50 +491,71 @@ module internal Memory = let guardedStatedMap mapper state term = commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id - + let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + // Returns PC containing only constraints dependent with cond + let keepDependentWith (pc : PC2.PathCondition) cond = + pc.Fragments + |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) + |> Option.defaultValue pc let execution thenState elseState condition k = assert (condition <> True && condition <> False) thenBranch thenState (fun thenResult -> elseBranch elseState (fun elseResult -> merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let thenPc = PC.add state.pc condition - let elsePc = PC.add state.pc (!!condition) - if PC.isFalse thenPc then + conditionInvocation state (fun (condition, conditionState) -> + let negatedCondition = !!condition + let thenPc = PC2.add state.pc condition + let elsePc = PC2.add state.pc negatedCondition + let independentThenPc = keepDependentWith thenPc condition + // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond + let independentElsePc = keepDependentWith elsePc negatedCondition + if independentThenPc.IsTrivialFalse then conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) - elif PC.isFalse elsePc then + elif independentElsePc.IsTrivialFalse then conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - conditionState.pc <- thenPc + conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc + conditionState.pc <- independentElsePc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> __insufficientInformation__ "Unable to witness branch" | SolverInteraction.SmtSat model -> + conditionState.pc <- elsePc conditionState.model <- Some model.mdl + StatedLogger.log conditionState.id $"Model stack: %s{model.mdl.state.stack.frames.ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> conditionState.pc <- elsePc + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model -> - conditionState.pc <- elsePc - conditionState.model <- Some model.mdl + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- independentElsePc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> conditionState.pc <- thenPc + conditionState.model <- Some thenModel.mdl + StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model -> + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- thenPc let thenState = conditionState let elseState = copy conditionState elsePc - elseState.model <- Some model.mdl - thenState.pc <- thenPc + StatedLogger.copy thenState.id elseState.id + StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + elseState.model <- Some elseModel.mdl + thenState.model <- Some thenModel.mdl execution thenState elseState condition k) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = @@ -721,75 +742,9 @@ module internal Memory = let private checkBlockBounds state reportError blockSize startByte endByte = let failCondition = simplifyGreater endByte blockSize id ||| simplifyLess startByte (makeNumber 0) id // NOTE: disables overflow in solver - state.pc <- PC.add state.pc (makeExpressionNoOvf failCondition id) + state.pc.Add (makeExpressionNoOvf failCondition id) reportErrorIfNeed state reportError failCondition - let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - // Returns PC containing only constraints dependent with cond - let keepDependentWith (pc : PC2.PathCondition) cond = - pc.Fragments - |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) - |> Option.defaultValue pc - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let negatedCondition = !!condition - let thenPc = PC2.add state.pc condition - let elsePc = PC2.add state.pc negatedCondition - let independentThenPc = keepDependentWith thenPc condition - // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond - let independentElsePc = keepDependentWith elsePc negatedCondition - if independentThenPc.IsTrivialFalse then - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - elif independentElsePc.IsTrivialFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - else - conditionState.pc <- independentThenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat model -> - conditionState.pc <- elsePc - conditionState.model <- Some model.mdl - StatedLogger.log conditionState.id $"Model stack: %s{model.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - conditionState.pc <- elsePc - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- thenPc - let thenState = conditionState - let elseState = copy conditionState elsePc - StatedLogger.copy thenState.id elseState.id - StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" - StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" - elseState.model <- Some elseModel.mdl - thenState.model <- Some thenModel.mdl - execution thenState elseState condition k) - let private readAddressUnsafe address startByte endByte = let size = Terms.sizeOf address match startByte.term, endByte.term with @@ -810,7 +765,7 @@ module internal Memory = and private readStructUnsafe fields structType startByte endByte = let readField fieldId = fields.[fieldId] - readFieldsUnsafe (makeEmpty()) (fun _ -> __unreachable__()) readField false structType startByte endByte + readFieldsUnsafe (State.makeEmpty None) (fun _ -> __unreachable__()) readField false structType startByte endByte and private getAffectedFields state reportError readField isStatic (blockType : symbolicType) startByte endByte = let t = toDotNetType blockType @@ -1137,7 +1092,7 @@ module internal Memory = and private writeStructUnsafe structTerm fields structType startByte value = let readField fieldId = fields.[fieldId] - let updatedFields = writeFieldsUnsafe (makeEmpty()) (fun _ -> __unreachable__()) readField false structType startByte value + let updatedFields = writeFieldsUnsafe (State.makeEmpty None) (fun _ -> __unreachable__()) readField false structType startByte value let writeField structTerm (fieldId, value) = writeStruct structTerm fieldId value List.fold writeField structTerm updatedFields diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 22f070d4e..f8d311297 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -719,7 +719,7 @@ module internal Z3 = let address = arr.Args |> Array.last |> x.DecodeConcreteHeapAddress t |> ConcreteHeapAddress HeapRef address t let address = fields |> List.fold (fun address field -> StructField(address, field)) address - let states = Memory.WriteSafe targetModel.state (Ref address) value + let states = Memory.Write targetModel.state (Ref address) value assert(states.Length = 1 && states.[0] = targetModel.state) elif arr.IsConst then () else internalfailf "Unexpected array expression in model: %O" arr diff --git a/VSharp.Utils/StatedLogger.fs b/VSharp.Utils/StatedLogger.fs index 6c32a2cf5..73eeb4fb4 100644 --- a/VSharp.Utils/StatedLogger.fs +++ b/VSharp.Utils/StatedLogger.fs @@ -7,7 +7,7 @@ open System.Text module StatedLogger = let stateLogs = Dictionary() - let public log stateId message = + let public log stateId (message : string) = if not <| stateLogs.ContainsKey(stateId) then stateLogs.Add(stateId, StringBuilder()) stateLogs.[stateId].AppendLine(message) |> ignore diff --git a/global.json b/global.json deleted file mode 100644 index e86bfecd4..000000000 --- a/global.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sdk": { - "version": "5.0.400" - } -} \ No newline at end of file From dc2170ca6394e19bae1b303c5e4f9946e773f814 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 12:23:50 +0300 Subject: [PATCH 41/88] [temp] Some code for testing purposes, fixme --- VSharp.SILI.Core/Memory.fs | 139 +++++++++++++++++++++++++++++++-- VSharp.Test/Tests/RegExTest.cs | 6 +- VSharp.Utils/Logger.fs | 2 +- VSharp.Utils/Stopwatch.fs | 14 ++-- 4 files changed, 146 insertions(+), 15 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 8a7522e3a..24bf9ea63 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -490,9 +490,136 @@ module internal Memory = let guardedStatedApply f state term = guardedStatedApplyk (Cps.ret2 f) state term id let guardedStatedMap mapper state term = - commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id - + commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id + let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let keepDependentWith (pc : PC2.PathCondition) cond = + pc.Fragments + |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) + |> Option.defaultValue pc + let execution thenState elseState condition k = + assert (condition <> True && condition <> False) + thenBranch thenState (fun thenResult -> + elseBranch elseState (fun elseResult -> + merge2Results thenResult elseResult |> k)) + conditionInvocation state (fun (condition, conditionState) -> + let negatedCondition = !!condition + let thenPc = PC2.add state.pc condition + let elsePc = PC2.add state.pc negatedCondition + let independentThenPc = keepDependentWith thenPc condition + // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond + let independentElsePc = keepDependentWith elsePc negatedCondition + if thenPc.IsTrivialFalse then + Stopwatch.runMeasuringTime "then_is_trivial_false" (fun () -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + ) + elif elsePc.IsTrivialFalse then + Stopwatch.runMeasuringTime "else_is_trivial_false" (fun () -> + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + ) + else + conditionState.pc <- independentThenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + Stopwatch.runMeasuringTime "branch_1" id + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat elseModel -> + Stopwatch.runMeasuringTime "branch_2" id + conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl + StatedLogger.log conditionState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + Stopwatch.runMeasuringTime "branch_3" id + conditionState.pc <- elsePc + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + Stopwatch.runMeasuringTime "branch_4" id + conditionState.pc <- thenPc + StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + Stopwatch.runMeasuringTime "branch_5" id + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState elsePc + elseState.model <- Some elseModel.mdl + thenState.pc <- thenPc + StatedLogger.copy thenState.id elseState.id + StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" + StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + execution thenState elseState condition k) + +(* let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let execution thenState elseState condition k = + assert (condition <> True && condition <> False) + thenBranch thenState (fun thenResult -> + elseBranch elseState (fun elseResult -> + merge2Results thenResult elseResult |> k)) + conditionInvocation state (fun (condition, conditionState) -> + let thenPc = PC2.add state.pc condition + let elsePc = PC2.add state.pc (!!condition) + if thenPc.IsTrivialFalse then + Stopwatch.runMeasuringTime "then_is_trivial_false" (fun () -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + ) + elif elsePc.IsTrivialFalse then + Stopwatch.runMeasuringTime "else_is_trivial_false" (fun () -> + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + ) + else + conditionState.pc <- thenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + Stopwatch.runMeasuringTime "branch_1" id + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat model -> + Stopwatch.runMeasuringTime "branch_2" id + conditionState.model <- Some model.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + Stopwatch.runMeasuringTime "branch_3" id + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- elsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + Stopwatch.runMeasuringTime "branch_4" id + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat model -> + Stopwatch.runMeasuringTime "branch_5" id + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState elsePc + elseState.model <- Some model.mdl + thenState.pc <- thenPc + execution thenState elseState condition k)*) + +(* let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = // Returns PC containing only constraints dependent with cond let keepDependentWith (pc : PC2.PathCondition) cond = pc.Fragments @@ -509,11 +636,11 @@ module internal Memory = let elsePc = PC2.add state.pc negatedCondition let independentThenPc = keepDependentWith thenPc condition // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond - let independentElsePc = keepDependentWith elsePc negatedCondition - if independentThenPc.IsTrivialFalse then + let independentElsePc = keepDependentWith elsePc negatedCondition + if thenPc.IsTrivialFalse then conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) - elif independentElsePc.IsTrivialFalse then + elif elsePc.IsTrivialFalse then conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else @@ -556,7 +683,7 @@ module internal Memory = StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" elseState.model <- Some elseModel.mdl thenState.model <- Some thenModel.mdl - execution thenState elseState condition k) + execution thenState elseState condition k)*) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k diff --git a/VSharp.Test/Tests/RegExTest.cs b/VSharp.Test/Tests/RegExTest.cs index d280823d3..cb310c834 100644 --- a/VSharp.Test/Tests/RegExTest.cs +++ b/VSharp.Test/Tests/RegExTest.cs @@ -51,9 +51,9 @@ public static bool Match(string re, string text) public class RegExTest { [TestSvm(100)] - public static string OwnImplementationTest(char c1, char c2, char c3, char c4, char c5, char c6) + public static string OwnImplementationTest111(char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8, char c9, char c10) { - string pattern = new string(new char[] {c1, c2, c3}); + string pattern = new string(new char[] {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10}); string result = ""; if (RegExImplementation.Match(pattern, "hello")) { @@ -108,7 +108,7 @@ public static bool OwnImplementationTest4(string pattern) return RegExImplementation.Match(pattern, "Hello"); } - // [TestSvm(100)] + //[TestSvm(100)] [Ignore("need more external method implementations")] public static MatchCollection SmallSystemImplementationTest() { diff --git a/VSharp.Utils/Logger.fs b/VSharp.Utils/Logger.fs index 3f3b6809e..3887b653f 100644 --- a/VSharp.Utils/Logger.fs +++ b/VSharp.Utils/Logger.fs @@ -8,7 +8,7 @@ module Logger = let Info = 3 let Trace = 4 - let mutable current_log_level = Error + let mutable current_log_level = Trace let mutable current_text_writer = Console.Out let public ConfigureWriter writer = current_text_writer <- writer diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index 501f87e2e..0a78d23ac 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -34,13 +34,17 @@ module Stopwatch = else let newMeasurement = { stopwatch = Stopwatch(); timesCalled = 0 } measurements.[tag] <- newMeasurement - newMeasurement - try - measurement.stopwatch.Start() + newMeasurement + if measurement.stopwatch.IsRunning then measurement.timesCalled <- measurement.timesCalled + 1 action() - finally - measurement.stopwatch.Stop() + else + try + measurement.stopwatch.Start() + measurement.timesCalled <- measurement.timesCalled + 1 + action() + finally + measurement.stopwatch.Stop() let public stopAll () = measurements From b6906b089932d72134ae3daf1ecc92a6de2a473a Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 15:57:38 +0300 Subject: [PATCH 42/88] [fix] Fix Slice folding --- VSharp.SILI.Core/Terms.fs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/VSharp.SILI.Core/Terms.fs b/VSharp.SILI.Core/Terms.fs index b3f556b75..ee73174a9 100644 --- a/VSharp.SILI.Core/Terms.fs +++ b/VSharp.SILI.Core/Terms.fs @@ -724,10 +724,7 @@ module internal Terms = | GuardedValues(gs, vs) -> foldSeq folder gs state |> foldSeq folder vs | Slice(t, s, e, pos) -> - let state = folder state t - let state = folder state s - let state = folder state e - folder state pos + foldSeq folder [t; s; e; pos] state | Union(terms) -> foldSeq folder (List.unzip terms |> (fun (l1, l2) -> List.append l1 l2)) state | _ -> state From 4db3cc33862fb8362acc5a801c23a51f411811d7 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 16:31:48 +0300 Subject: [PATCH 43/88] [style] Fix pc namings --- VSharp.SILI.Core/API.fs | 15 +++++------ VSharp.SILI.Core/API.fsi | 10 +++---- VSharp.SILI.Core/Copying.fs | 7 ++--- VSharp.SILI.Core/Memory.fs | 26 +++++++++---------- VSharp.SILI.Core/Merging.fs | 7 +++-- .../{NewPathCondition.fs => PathCondition.fs} | 20 +++++++------- VSharp.SILI.Core/State.fs | 4 +-- VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 2 +- VSharp.SILI/Statistics.fs | 2 +- 9 files changed, 44 insertions(+), 49 deletions(-) rename VSharp.SILI.Core/{NewPathCondition.fs => PathCondition.fs} (95%) diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 94a473ab1..ea2304ce9 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -229,13 +229,12 @@ module API = | _ -> internalfailf "Unboxing: expected heap reference, but got %O" reference let AddConstraint conditionState condition = Memory.addConstraint conditionState condition - let IsFalsePathCondition conditionState = conditionState.pc.IsTrivialFalse + let IsFalsePathCondition conditionState = conditionState.pc.IsFalse let Contradicts state condition = - let copy = state.pc.Copy() - copy.Add condition - copy.IsTrivialFalse - let PathConditionToSeq (pc : PC2.PathCondition) = pc.ToSeq() - let EmptyPathCondition() = PC2.PathCondition() + let copy = PC.add condition state.pc + copy.IsFalse + let PathConditionToSeq (pc : PC.PathCondition) = pc.ToSeq() + let EmptyPathCondition() = PC.PathCondition() module Types = let Numeric t = Types.Numeric t @@ -585,7 +584,7 @@ module API = | _ -> internalfailf "constructing string from char array: expected string reference, but got %O" dstRef let ComposeStates state state' = Memory.composeStates state state' - let WLP state (pc' : PC2.PathCondition) = (pc'.Map (Memory.fillHoles state)).UnionWith state.pc + let WLP state (pc' : PC.PathCondition) = (pc'.Map (Memory.fillHoles state)).UnionWith state.pc let Merge2States (s1 : state) (s2 : state) = Memory.merge2States s1 s2 let Merge2Results (r1, s1 : state) (r2, s2 : state) = Memory.merge2Results (r1, s1) (r2, s2) @@ -613,4 +612,4 @@ module API = module Print = let Dump state = Memory.dump state - let PrintPC (pc : PC2.PathCondition) = pc.ToString() + let PrintPC (pc : PC.PathCondition) = pc.ToString() diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 374a51390..ca62a04eb 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -18,7 +18,7 @@ module API = val StatedConditionalExecutionAppendResults : (state -> (state -> (term * state -> 'a) -> 'b) -> (state -> (state list -> 'a) -> 'a) -> (state -> (state list -> 'a) -> 'a) -> (state list -> 'a) -> 'b) val GuardedApplyExpression : term -> (term -> term) -> term - val GuardedApplyExpressionWithPC : PC2.PathCondition -> term -> (term -> term) -> term + val GuardedApplyExpressionWithPC : PC.PathCondition -> term -> (term -> term) -> term val GuardedStatedApplyStatementK : state -> term -> (state -> term -> (term * state -> 'a) -> 'a) -> ((term * state) list -> 'a) -> 'a val GuardedStatedApplyk : (state -> term -> ('item -> 'a) -> 'a) -> state -> term -> ('item list -> 'item list) -> ('item list -> 'a) -> 'a @@ -105,8 +105,8 @@ module API = val AddConstraint : state -> term -> unit val IsFalsePathCondition : state -> bool val Contradicts : state -> term -> bool - val PathConditionToSeq : PC2.PathCondition -> term seq - val EmptyPathCondition : unit -> PC2.PathCondition + val PathConditionToSeq : PC.PathCondition -> term seq + val EmptyPathCondition : unit -> PC.PathCondition module Types = val Numeric : System.Type -> symbolicType @@ -286,7 +286,7 @@ module API = // TODO: get rid of all unnecessary stuff below! val ComposeStates : state -> state -> state list - val WLP : state -> PC2.PathCondition -> PC2.PathCondition + val WLP : state -> PC.PathCondition -> PC.PathCondition val Merge2States : state -> state -> state list val Merge2Results : term * state -> term * state -> (term * state) list @@ -297,7 +297,7 @@ module API = module Print = val Dump : state -> string - val PrintPC : PC2.PathCondition -> string + val PrintPC : PC.PathCondition -> string // module Marshalling = // val Unmarshal : state -> obj -> term * state diff --git a/VSharp.SILI.Core/Copying.fs b/VSharp.SILI.Core/Copying.fs index 575a6f3a0..1233f4a19 100644 --- a/VSharp.SILI.Core/Copying.fs +++ b/VSharp.SILI.Core/Copying.fs @@ -24,11 +24,8 @@ module internal Copying = let constant = Constant "i" source Types.Int32 let leftBound = simplifyLessOrEqual lowerBound constant id let rightBound = simplifyLessOrEqual constant upperBound id - let pcWithBounds = - let copy = state.pc.Copy() - copy.Add leftBound - copy.Add rightBound - copy + let pcWithBounds = + PC.add leftBound state.pc |> PC.add rightBound state.pc <- pcWithBounds constant diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 24bf9ea63..df75642b9 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -473,8 +473,8 @@ module internal Memory = match term.term with | Union gvs -> let filterUnsat (g, v) k = - let pc = PC2.add state.pc g - if pc.IsTrivialFalse then k None + let pc = PC.add g state.pc + if pc.IsFalse then k None else Some (pc, v) |> k Cps.List.choosek filterUnsat gvs (fun pcs -> match pcs with @@ -493,7 +493,7 @@ module internal Memory = commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let keepDependentWith (pc : PC2.PathCondition) cond = + let keepDependentWith (pc : PC.PathCondition) cond = pc.Fragments |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) |> Option.defaultValue pc @@ -504,17 +504,17 @@ module internal Memory = merge2Results thenResult elseResult |> k)) conditionInvocation state (fun (condition, conditionState) -> let negatedCondition = !!condition - let thenPc = PC2.add state.pc condition - let elsePc = PC2.add state.pc negatedCondition + let thenPc = PC.add condition state.pc + let elsePc = PC.add negatedCondition state.pc let independentThenPc = keepDependentWith thenPc condition // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond let independentElsePc = keepDependentWith elsePc negatedCondition - if thenPc.IsTrivialFalse then + if thenPc.IsFalse then Stopwatch.runMeasuringTime "then_is_trivial_false" (fun () -> conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) ) - elif elsePc.IsTrivialFalse then + elif elsePc.IsFalse then Stopwatch.runMeasuringTime "else_is_trivial_false" (fun () -> conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) @@ -534,12 +534,12 @@ module internal Memory = conditionState.pc <- elsePc conditionState.model <- Some elseModel.mdl StatedLogger.log conditionState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> Stopwatch.runMeasuringTime "branch_3" id conditionState.pc <- elsePc - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat thenModel -> conditionState.pc <- independentElsePc @@ -549,7 +549,7 @@ module internal Memory = Stopwatch.runMeasuringTime "branch_4" id conditionState.pc <- thenPc StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> Stopwatch.runMeasuringTime "branch_5" id @@ -561,8 +561,8 @@ module internal Memory = StatedLogger.copy thenState.id elseState.id StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" - StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" + StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" + StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" execution thenState elseState condition k) (* let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = @@ -1629,7 +1629,7 @@ module internal Memory = if not <| isFalse g then return { id = Guid.NewGuid().ToString() - pc = if isTrue g then pc else PC2.add pc g + pc = if isTrue g then pc else PC.add g pc evaluationStack = evaluationStack exceptionsRegister = exceptionRegister stack = stack diff --git a/VSharp.SILI.Core/Merging.fs b/VSharp.SILI.Core/Merging.fs index 656b72688..520dc7a06 100644 --- a/VSharp.SILI.Core/Merging.fs +++ b/VSharp.SILI.Core/Merging.fs @@ -102,11 +102,10 @@ module internal Merging = let guardedApplyk f term k = commonGuardedApplyk f term merge k let guardedApply f term = guardedApplyk (Cps.ret f) term id - let commonGuardedMapkWithPC (pc : PC2.PathCondition) mapper gvs merge k = + let commonGuardedMapkWithPC (pc : PC.PathCondition) mapper gvs merge k = let foldFunc gvs (g, v) k = - let pc' = pc.Copy() - pc'.Add(g) - if pc'.IsTrivialFalse then k gvs + let pc' = PC.add g pc + if pc'.IsFalse then k gvs else mapper v (fun t -> k ((g, t) :: gvs)) Cps.List.foldlk foldFunc [] gvs (merge >> k) diff --git a/VSharp.SILI.Core/NewPathCondition.fs b/VSharp.SILI.Core/PathCondition.fs similarity index 95% rename from VSharp.SILI.Core/NewPathCondition.fs rename to VSharp.SILI.Core/PathCondition.fs index a3df4fa04..aa229584e 100644 --- a/VSharp.SILI.Core/NewPathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -3,8 +3,8 @@ open VSharp open VSharp.Utils open System.Collections.Generic -module public PC2 = - +module public PC = + type private node = | Tail of term * term pset | Node of term @@ -16,9 +16,9 @@ module public PC2 = | Node(term) -> term | Empty -> invalidOp "Cannot unwrap empty node" - type public PathCondition private(constants : Dictionary, constraints : HashSet, isTrivialFalse : bool) = + type public PathCondition private(constants : Dictionary, constraints : HashSet, isFalse : bool) = - let mutable isTrivialFalse = isTrivialFalse + let mutable isFalse = isFalse let nextNode term = let mutable found = Empty @@ -63,7 +63,7 @@ module public PC2 = let becomeTrivialFalse() = constants.Clear() constraints.Clear() - isTrivialFalse <- true + isFalse <- true let addSubset (constants : Dictionary) constantsToAdd constraintsToAdd = if Seq.isEmpty constantsToAdd then @@ -133,10 +133,10 @@ module public PC2 = member this.Copy() = let inner() = - PathCondition(Dictionary(constants), HashSet(constraints), isTrivialFalse) + PathCondition(Dictionary(constants), HashSet(constraints), isFalse) Stopwatch.runMeasuringTime "PC_Copy" inner - member this.IsTrivialFalse = isTrivialFalse + member this.IsFalse = isFalse member this.IsEmpty = constraints.Count = 0 @@ -147,7 +147,7 @@ module public PC2 = match newConstraint with | True -> () | False -> becomeTrivialFalse() - | _ when isTrivialFalse -> () + | _ when isFalse -> () | _ when constraints.Contains(newConstraint) -> () // what if constraint is not equal to newConstraint structurally, but is equal logically? | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() @@ -166,7 +166,7 @@ module public PC2 = member this.Fragments = let inner() = - if isTrivialFalse then + if isFalse then Seq.singleton this else let getSubsetByRepresentative = @@ -180,7 +180,7 @@ module public PC2 = Seq.choose getSubsetByRepresentative constants.Values Stopwatch.runMeasuringTime "PC_Fragments" inner - let public add (pc : PathCondition) newConstraint = + let public add newConstraint (pc : PathCondition) = let copy = pc.Copy() copy.Add newConstraint copy diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 2f58ff3f3..a3444afbc 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -117,7 +117,7 @@ and [] state = { id : string - mutable pc : PC2.PathCondition + mutable pc : PC.PathCondition mutable evaluationStack : evaluationStack mutable stack : callStack // Arguments and local variables mutable stackBuffers : pdict // Buffers allocated via stackAlloc @@ -147,7 +147,7 @@ and module public State = let makeEmpty modelState = { id = Guid.NewGuid().ToString() - pc = PC2.PathCondition() + pc = PC.PathCondition() evaluationStack = EvaluationStack.empty exceptionsRegister = NoException stack = CallStack.empty diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index 057809d1d..1f4ef5d78 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -21,7 +21,7 @@ - + diff --git a/VSharp.SILI/Statistics.fs b/VSharp.SILI/Statistics.fs index f42a797b1..f14319f15 100644 --- a/VSharp.SILI/Statistics.fs +++ b/VSharp.SILI/Statistics.fs @@ -12,7 +12,7 @@ open VSharp.Utils open CilStateOperations open ipOperations -type pob = {loc : codeLocation; lvl : uint; pc : PC2.PathCondition} +type pob = {loc : codeLocation; lvl : uint; pc : PC.PathCondition} with override x.ToString() = sprintf "loc = %O; lvl = %d; pc = %s" x.loc x.lvl (Print.PrintPC x.pc) From b8e663d992341398f769025e1f163a6142d478ac Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 16:48:37 +0300 Subject: [PATCH 44/88] [fix] Remove unused persistent union find --- VSharp.Test/PersistentUnionFindTests.cs | 141 ------------------------ VSharp.Utils/PersistentUnionFind.fs | 109 ------------------ VSharp.Utils/VSharp.Utils.fsproj | 1 - 3 files changed, 251 deletions(-) delete mode 100644 VSharp.Test/PersistentUnionFindTests.cs delete mode 100644 VSharp.Utils/PersistentUnionFind.fs diff --git a/VSharp.Test/PersistentUnionFindTests.cs b/VSharp.Test/PersistentUnionFindTests.cs deleted file mode 100644 index ddfb12ffe..000000000 --- a/VSharp.Test/PersistentUnionFindTests.cs +++ /dev/null @@ -1,141 +0,0 @@ -using NUnit.Framework; -using System; -using System.Collections.Generic; -using static VSharp.PersistentUnionFind; - -namespace VSharp.Test -{ - [TestFixture] - public sealed class PersistentUnionFindTests - { - private const string foo = "foo"; - private const string bar = "bar"; - private const string baz = "baz"; - - private pUnionFind stringUnionFind; - private pUnionFind intUnionFind; - - [SetUp] - public void SetUp() - { - stringUnionFind = empty(); - intUnionFind = empty(); - } - - [Test] - public void SingleElementSetsParentsTest() - { - Add(ref stringUnionFind, foo); - Add(ref stringUnionFind, bar); - var fooParent = find(foo, stringUnionFind); - var barParent = find(bar, stringUnionFind); - Assert.AreEqual(foo, fooParent); - Assert.AreEqual(bar, barParent); - } - - [Test] - public void ElementsOfUnionHaveSameParentTest1() - { - Add(ref stringUnionFind, foo); - Add(ref stringUnionFind, bar); - Add(ref stringUnionFind, baz); - Union(foo, bar, ref stringUnionFind); - Union(foo, baz, ref stringUnionFind); - var fooParent = find(foo, stringUnionFind); - var barParent = find(bar, stringUnionFind); - var bazParent = find(baz, stringUnionFind); - Assert.AreEqual(fooParent, barParent); - Assert.AreEqual(barParent, bazParent); - } - - [Test] - public void ElementsOfUnionHaveSameParentTest2() - { - Add(ref intUnionFind, 1); - Add(ref intUnionFind, 2); - - for (var i = 3; i <= 100; ++i) - { - Add(ref intUnionFind, i); - Union(i, 2 - i % 2, ref intUnionFind); - } - - var parent1 = find(1, intUnionFind); - var parent2 = find(2, intUnionFind); - - for (var i = 1; i <= 100; ++i) - { - var actualParent = find(i, intUnionFind); - var expectedParent = i % 2 == 0 ? parent2 : parent1; - Assert.AreEqual(expectedParent, actualParent); - } - - Union(21, 54, ref intUnionFind); - - var unionParent = find(1, intUnionFind); - - for (var i = 1; i <= 100; ++i) - { - var actualParent = find(i, intUnionFind); - Assert.AreEqual(unionParent, actualParent); - } - } - - [Test] - public void SubsetTest() - { - Add(ref stringUnionFind, foo); - Add(ref stringUnionFind, bar); - Add(ref stringUnionFind, baz); - Union(foo, bar, ref stringUnionFind); - var fooBarSubset = new List(toSeq(subset(foo, stringUnionFind))); - var bazSubset = new List(toSeq(subset(baz, stringUnionFind))); - var expectedFooBarSubset = new List { foo, bar }; - var expectedBazSubset = new List { baz }; - Assert.That(fooBarSubset, Is.EquivalentTo(expectedFooBarSubset)); - Assert.That(bazSubset, Is.EquivalentTo(expectedBazSubset)); - } - - [Test] - public void FindForNonexistentElementThrowsTest() - { - Add(ref stringUnionFind, foo); - Assert.Throws(() => find(bar, stringUnionFind)); - } - - [Test] - public void TryFindTest() - { - Add(ref stringUnionFind, foo); - var found = tryFind(foo, stringUnionFind).Value; - Assert.AreEqual(foo, found); - } - - [Test] - public void TryFindForNonexistentElementReturnsNoneTest() - { - Add(ref stringUnionFind, foo); - Assert.Throws(() => _ = tryFind(bar, stringUnionFind).Value); - } - - [Test] - public void AddExistentElementDoesNothingTest() - { - Add(ref stringUnionFind, foo); - Add(ref stringUnionFind, foo); - var actualElements = new List(toSeq(stringUnionFind)); - var expectedElements = new List { foo }; - Assert.That(actualElements, Is.EquivalentTo(expectedElements)); - } - - private void Add (ref pUnionFind unionFind, T element) - { - unionFind = add(unionFind, element); - } - - private void Union (T one, T another, ref pUnionFind unionFind) - { - unionFind = union(one, another, unionFind); - } - } -} diff --git a/VSharp.Utils/PersistentUnionFind.fs b/VSharp.Utils/PersistentUnionFind.fs deleted file mode 100644 index 585a97123..000000000 --- a/VSharp.Utils/PersistentUnionFind.fs +++ /dev/null @@ -1,109 +0,0 @@ -namespace VSharp - -open System -open VSharp - -(* - Union-find sets are implemented with dictionary cyclically mapping previous - value to the next value wrapped with node. The last element (i. e. element before Tail) - is considered as representative parent element of the set -*) -type private node<'a> = - | Tail of 'a - | Node of 'a - -/// -/// Persistent union-find (disjoint-set) structure -/// -type public pUnionFind<'a> when 'a : equality = - private {elements : pdict<'a, node<'a>>} - -module public PersistentUnionFind = - - let public empty<'a when 'a : equality> : pUnionFind<'a> = - {elements = PersistentDict.empty} - - let public toSeq puf = PersistentDict.keys puf.elements - - /// - /// Returns representative element of the set containing the given element or - /// throws if the given element not found - /// - /// Element not found - let rec public find a puf = - Stopwatch.runMeasuringTime "puf_find" (fun () -> - try - PersistentDict.find puf.elements a - |> function - | Tail _ -> a - | Node(next) -> find next puf - with - _ -> raise (InvalidOperationException "Element not found") - ) - - - /// - /// Returns representative element of the set containing the given element or - /// None if the given element not found - /// - let public tryFind a puf = - try - find a puf |> Some - with - _ -> None - - let private unwrapNode = function - | Tail value -> value - | Node value -> value - - /// - /// Unions two sets containing the given elements - /// - let public union a b puf = - Stopwatch.runMeasuringTime "puf_union" (fun () -> - let aParentOption = tryFind a puf - let bParentOption = tryFind b puf - match aParentOption, bParentOption with - | Some(aParent), Some(bParent) when aParent <> bParent -> - let aTail = PersistentDict.find puf.elements aParent |> unwrapNode - let bTail = PersistentDict.find puf.elements bParent |> unwrapNode - let mergedElements = - puf.elements - |> PersistentDict.add aParent (Tail(bTail)) - |> PersistentDict.add bParent (Node(aTail)) - {elements = mergedElements} - | _ -> puf - ) - - - /// - /// Adds a single-element set containing the given element. If the element already exists, - /// does nothing - /// - let public add puf a = - Stopwatch.runMeasuringTime "puf_add" (fun () -> - match tryFind a puf with - | Some _ -> puf - | None -> {elements = PersistentDict.add a (Tail(a)) puf.elements} - ) - - /// - /// Returns a single-set union-find with the set containing the given element - /// - /// Element not found - let public subset a puf = - Stopwatch.runMeasuringTime "puf_subset" (fun () -> - let rec traverse current acc = - let next = - try - PersistentDict.find puf.elements current - with - _ -> raise (InvalidOperationException "Element not found") - let updatedDict = PersistentDict.add current next acc - let unwrappedNext = unwrapNode next - if (unwrappedNext <> a) then - traverse unwrappedNext updatedDict - else updatedDict - let subsetElements = traverse a PersistentDict.empty - {elements = subsetElements} - ) diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 7136fc601..4d568f9fe 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -36,7 +36,6 @@ - From 25af3e97e03189d7cfc1c3c0dad8f469bad77a58 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 16:50:44 +0300 Subject: [PATCH 45/88] [style] Remove commented code --- VSharp.SILI.Core/Memory.fs | 120 ------------------------------------- 1 file changed, 120 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index df75642b9..b142b499c 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -565,126 +565,6 @@ module internal Memory = StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" execution thenState elseState condition k) -(* let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let thenPc = PC2.add state.pc condition - let elsePc = PC2.add state.pc (!!condition) - if thenPc.IsTrivialFalse then - Stopwatch.runMeasuringTime "then_is_trivial_false" (fun () -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - ) - elif elsePc.IsTrivialFalse then - Stopwatch.runMeasuringTime "else_is_trivial_false" (fun () -> - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - ) - else - conditionState.pc <- thenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - Stopwatch.runMeasuringTime "branch_1" id - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat model -> - Stopwatch.runMeasuringTime "branch_2" id - conditionState.model <- Some model.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - Stopwatch.runMeasuringTime "branch_3" id - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- elsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - Stopwatch.runMeasuringTime "branch_4" id - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat model -> - Stopwatch.runMeasuringTime "branch_5" id - conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState elsePc - elseState.model <- Some model.mdl - thenState.pc <- thenPc - execution thenState elseState condition k)*) - -(* let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - // Returns PC containing only constraints dependent with cond - let keepDependentWith (pc : PC2.PathCondition) cond = - pc.Fragments - |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) - |> Option.defaultValue pc - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let negatedCondition = !!condition - let thenPc = PC2.add state.pc condition - let elsePc = PC2.add state.pc negatedCondition - let independentThenPc = keepDependentWith thenPc condition - // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond - let independentElsePc = keepDependentWith elsePc negatedCondition - if thenPc.IsTrivialFalse then - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - elif elsePc.IsTrivialFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - else - conditionState.pc <- independentThenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat model -> - conditionState.pc <- elsePc - conditionState.model <- Some model.mdl - StatedLogger.log conditionState.id $"Model stack: %s{model.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - conditionState.pc <- elsePc - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- thenPc - let thenState = conditionState - let elseState = copy conditionState elsePc - StatedLogger.copy thenState.id elseState.id - StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC2.toSeq |> conjunction).ToString()}" - StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC2.toSeq |> conjunction).ToString()}" - elseState.model <- Some elseModel.mdl - thenState.model <- Some thenModel.mdl - execution thenState elseState condition k)*) - let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k let statedConditionalExecutionWithMerge state conditionInvocation thenBranch elseBranch = From 6aa02e784d9ad0b272a0d175244a0042abe5e4e5 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 18:32:04 +0300 Subject: [PATCH 46/88] [style] Add comments to utils and PC --- VSharp.SILI.Core/Memory.fs | 20 +++++----- VSharp.SILI.Core/PathCondition.fs | 51 +++++++++++++++++++++----- VSharp.SILI/Interpreter.fs | 2 +- VSharp.Utils/StatedLogger.fs | 20 ---------- VSharp.Utils/Stopwatch.fs | 61 +++++++++++++++++++------------ VSharp.Utils/TaggedLogger.fs | 39 ++++++++++++++++++++ VSharp.Utils/UnitTests.fs | 2 +- VSharp.Utils/VSharp.Utils.fsproj | 2 +- 8 files changed, 131 insertions(+), 66 deletions(-) delete mode 100644 VSharp.Utils/StatedLogger.fs create mode 100644 VSharp.Utils/TaggedLogger.fs diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index b142b499c..66264dfa0 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -533,13 +533,13 @@ module internal Memory = Stopwatch.runMeasuringTime "branch_2" id conditionState.pc <- elsePc conditionState.model <- Some elseModel.mdl - StatedLogger.log conditionState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + TaggedLogger.log conditionState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" + TaggedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> Stopwatch.runMeasuringTime "branch_3" id conditionState.pc <- elsePc - StatedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + TaggedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat thenModel -> conditionState.pc <- independentElsePc @@ -548,8 +548,8 @@ module internal Memory = | SolverInteraction.SmtUnknown _ -> Stopwatch.runMeasuringTime "branch_4" id conditionState.pc <- thenPc - StatedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" + TaggedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + TaggedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> Stopwatch.runMeasuringTime "branch_5" id @@ -558,11 +558,11 @@ module internal Memory = let elseState = copy conditionState elsePc elseState.model <- Some elseModel.mdl thenState.pc <- thenPc - StatedLogger.copy thenState.id elseState.id - StatedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - StatedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" - StatedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" + TaggedLogger.copy thenState.id elseState.id + TaggedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" + TaggedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" + TaggedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" + TaggedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" execution thenState elseState condition k) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index aa229584e..60da8f03d 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -12,25 +12,46 @@ module public PC = let private unwrapNode = function - | Tail(term, _) -> term - | Node(term) -> term + | Tail(constant, _) -> constant + | Node(constant) -> constant | Empty -> invalidOp "Cannot unwrap empty node" + (* + Path condition maintains independent subsets of constants and constraints ("constraint independence") + + constants -- dictionary used as union-find structure for constants. Constants of one subset are + cyclically mapping to each other. There is only one node.Tail in subset and it is the representative + element of the subset. Tail also contains the constraints corresponding to the constants subset + + constraints -- contains all constraints of the PC. Used to avoid picking constraints from subsets each time + when the complete PC is needed + + isFalse -- flag used to determine if the PC is false trivially (i. e. c an !c were added to it). + Invariant: PC doesn't contain True or False as elements. + *) type public PathCondition private(constants : Dictionary, constraints : HashSet, isFalse : bool) = let mutable isFalse = isFalse - let nextNode term = + let nextNode constant = let mutable found = Empty - constants.TryGetValue(term, &found) |> ignore + constants.TryGetValue(constant, &found) |> ignore found - let rec findPrevious term = - match nextNode term with - | Tail _ -> Some(term) + /// + /// Find operation of the union-find structure. Returns not the representative element, but the element before it + /// (for convenience) + /// + let rec findPrevious constant = + match nextNode constant with + | Tail _ -> Some(constant) | Node nextTerm -> findPrevious nextTerm | Empty -> None + /// + /// Union operation of the union-find structure. Subsets containing oneConstant and anotherConstant are merged. + /// oneConstant and anotherConstant don't need to be the representatives + /// let union oneConstant anotherConstant = match (findPrevious oneConstant), (findPrevious anotherConstant) with | Some(onePrevious), Some(anotherPrevious) -> @@ -43,6 +64,9 @@ module public PC = | _ -> () | _ -> invalidOp "Constant not found in dictionary" + /// + /// Returns union-find subset (of constants) containing the specified constant + /// let subset constantInSubset = seq { let rec inner currentConstant = @@ -65,9 +89,13 @@ module public PC = constraints.Clear() isFalse <- true + /// + /// Adds a cyclic subset to the union-find structure + /// + /// Union-find structure + /// Constants to add as nodes to the subset + /// Constraints to add to the tail let addSubset (constants : Dictionary) constantsToAdd constraintsToAdd = - if Seq.isEmpty constantsToAdd then - Logger.info "kek" let firstConstant = constantsToAdd |> Seq.head if Seq.length constantsToAdd = 1 then constants.[firstConstant] <- Tail(firstConstant, constraintsToAdd) @@ -164,6 +192,10 @@ module public PC = anotherPc.ToSeq() |> Seq.iter union.Add union + /// + /// Returns the sequence of path conditions such that constants contained in + /// one path condition are independent with constants contained in another one + /// member this.Fragments = let inner() = if isFalse then @@ -186,4 +218,3 @@ module public PC = copy let public toSeq (pc : PathCondition) = pc.ToSeq() - \ No newline at end of file diff --git a/VSharp.SILI/Interpreter.fs b/VSharp.SILI/Interpreter.fs index a0b565421..bf2843cec 100644 --- a/VSharp.SILI/Interpreter.fs +++ b/VSharp.SILI/Interpreter.fs @@ -1951,7 +1951,7 @@ type internal ILInterpreter(isConcolicMode : bool) as this = | Instruction(offset, m) -> if offset = 0 then Logger.printLogLazy Logger.Info "Starting to explore method %O" (lazy Reflection.getFullMethodName m) - StatedLogger.log cilState.state.id $"In method: %O{Reflection.getFullMethodName m}" + TaggedLogger.log cilState.state.id $"In method: %O{Reflection.getFullMethodName m}" x.ExecuteInstruction m offset cilState |> k | Exit m -> exit m diff --git a/VSharp.Utils/StatedLogger.fs b/VSharp.Utils/StatedLogger.fs deleted file mode 100644 index 73eeb4fb4..000000000 --- a/VSharp.Utils/StatedLogger.fs +++ /dev/null @@ -1,20 +0,0 @@ -namespace VSharp - -open System -open System.Collections.Generic -open System.IO -open System.Text - -module StatedLogger = - let stateLogs = Dictionary() - let public log stateId (message : string) = - if not <| stateLogs.ContainsKey(stateId) then stateLogs.Add(stateId, StringBuilder()) - stateLogs.[stateId].AppendLine(message) |> ignore - - let public copy fromStateId toStateId = - let from = stateLogs.GetValueOrDefault(fromStateId, StringBuilder()).ToString() - stateLogs.[toStateId] <- StringBuilder().AppendLine(from) - - let public saveLog stateId path = - let log = stateLogs.GetValueOrDefault(stateId, StringBuilder()).ToString() - File.WriteAllText(path, log) \ No newline at end of file diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index 0a78d23ac..f83acc22b 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -8,9 +8,12 @@ open System.IO open CsvHelper open CsvHelper.Configuration +/// +/// Contains functions for running code with time measurement and measurement results export +/// module Stopwatch = - type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } + type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } type private csvRecord = { commitHash: string @@ -23,10 +26,29 @@ module Stopwatch = } let private csvPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VSharpBenchmark") - let private csvFilename = "benchmark.csv" - + let private csvFilename = "benchmark.csv" let private measurements = Dictionary() + let private getGitCommitHash () = + let procStartInfo = ProcessStartInfo("git", "rev-parse --short HEAD") + + procStartInfo.RedirectStandardOutput <- true + procStartInfo.UseShellExecute <- false + procStartInfo.CreateNoWindow <- true + + use proc = new Process() + proc.StartInfo <- procStartInfo + + proc.Start() |> ignore + proc.WaitForExit() + + proc.StandardOutput.ReadLine() + + /// + /// Runs function saving its execution time + /// + /// Tag to save results with. Results from multiple runs with the same tag are added up + /// Function to run let public runMeasuringTime tag action = let measurement = if (measurements.ContainsKey tag) then @@ -46,28 +68,18 @@ module Stopwatch = finally measurement.stopwatch.Stop() + /// + /// Stops all running measurements + /// let public stopAll () = measurements |> Seq.map (|KeyValue|) - |> Seq.iter (fun (_, m) -> - m.stopwatch.Stop() - ) - - let private getGitCommitHash () = - let procStartInfo = ProcessStartInfo("git", "rev-parse --short HEAD") - - procStartInfo.RedirectStandardOutput <- true - procStartInfo.UseShellExecute <- false - procStartInfo.CreateNoWindow <- true - - use proc = new Process() - proc.StartInfo <- procStartInfo - - proc.Start() |> ignore - proc.WaitForExit() - - proc.StandardOutput.ReadLine() - + |> Seq.iter (fun (_, m) -> m.stopwatch.Stop()) + + /// + /// Saves all current measurement results to .csv file. If the file already exists, appends lines + /// + /// Additional tag given to the saved measurements let public saveMeasurements caseName = stopAll() @@ -100,5 +112,8 @@ module Stopwatch = use csvWriter = new CsvWriter(streamWriter, configuration) csvWriter.WriteRecords(records) - + + /// + /// Clears all current measurements + /// let public clear () = measurements.Clear() diff --git a/VSharp.Utils/TaggedLogger.fs b/VSharp.Utils/TaggedLogger.fs new file mode 100644 index 000000000..61cc2580f --- /dev/null +++ b/VSharp.Utils/TaggedLogger.fs @@ -0,0 +1,39 @@ +namespace VSharp + +open System +open System.Collections.Generic +open System.IO +open System.Text + +/// +/// Contains functions for saving logs with tag keys. +/// May be used to save logs from different states separately +/// +module TaggedLogger = + + let logs = Dictionary() + + /// + /// Saves log with the tag + /// + /// Tag to save the log with + /// Message to log + let public log tag (message : string) = + if not <| logs.ContainsKey(tag) then logs.Add(tag, StringBuilder()) + logs.[tag].AppendLine(message) |> ignore + + /// + /// Saves with toTag all logs with fromTag + /// + let public copy fromTag toTag = + let from = logs.GetValueOrDefault(fromTag, StringBuilder()).ToString() + logs.[toTag] <- StringBuilder().AppendLine(from) + + /// + /// Saves all logs with the specified tag to the file + /// + /// Tag to save the logs with + /// Path of the file to save + let public saveLog tag path = + let log = logs.GetValueOrDefault(tag, StringBuilder()).ToString() + File.WriteAllText(path, log) diff --git a/VSharp.Utils/UnitTests.fs b/VSharp.Utils/UnitTests.fs index 28093c138..70f01f2b6 100644 --- a/VSharp.Utils/UnitTests.fs +++ b/VSharp.Utils/UnitTests.fs @@ -33,7 +33,7 @@ type UnitTests(outputDir : string) = member x.GenerateTest (test : UnitTest) = testNumber <- testNumber + 1u if test.StateId.IsSome then - StatedLogger.saveLog test.StateId.Value $"%s{currentDir.FullName}%c{Path.DirectorySeparatorChar}info%s{testNumber.ToString()}.txt" + TaggedLogger.saveLog test.StateId.Value $"%s{currentDir.FullName}%c{Path.DirectorySeparatorChar}info%s{testNumber.ToString()}.txt" generateTest test ("test" + testNumber.ToString()) member x.GenerateError (test : UnitTest) = diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index 4d568f9fe..8daf2334c 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -19,7 +19,7 @@ - + From 747a36c4de6c77872b4d814288aaf5ffcd825655 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 22:12:41 +0300 Subject: [PATCH 47/88] [feat] Add functional map and union for PC --- VSharp.SILI.Core/API.fs | 2 +- VSharp.SILI.Core/Memory.fs | 2 +- VSharp.SILI.Core/PathCondition.fs | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index ea2304ce9..a2e4b143f 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -584,7 +584,7 @@ module API = | _ -> internalfailf "constructing string from char array: expected string reference, but got %O" dstRef let ComposeStates state state' = Memory.composeStates state state' - let WLP state (pc' : PC.PathCondition) = (pc'.Map (Memory.fillHoles state)).UnionWith state.pc + let WLP state (pc' : PC.PathCondition) = PC.map (Memory.fillHoles state) pc' |> PC.unionWith state.pc let Merge2States (s1 : state) (s2 : state) = Memory.merge2States s1 s2 let Merge2Results (r1, s1 : state) (r2, s2 : state) = Memory.merge2Results (r1, s1) (r2, s2) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 66264dfa0..bcb8ac026 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -1486,7 +1486,7 @@ module internal Memory = assert(not <| VectorTime.isEmpty state.currentTime) // TODO: do nothing if state is empty! list { - let pc = (state'.pc.Map (fillHoles state)).UnionWith state.pc + let pc = PC.map (fillHoles state) state'.pc |> PC.unionWith state.pc // Note: this is not final evaluationStack of resulting cilState, here we forget left state's opStack at all let evaluationStack = composeEvaluationStacksOf state state'.evaluationStack let exceptionRegister = composeRaisedExceptionsOf state state'.exceptionsRegister diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 60da8f03d..373ac282b 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -218,3 +218,7 @@ module public PC = copy let public toSeq (pc : PathCondition) = pc.ToSeq() + + let public map mapper (pc : PathCondition) = pc.Map mapper + + let public unionWith anotherPc (pc : PathCondition) = pc.UnionWith anotherPc From fbfcf512e2adfbb3741f4c5cd3f29c3f683f3c92 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 22:34:41 +0300 Subject: [PATCH 48/88] [fix] Remove time measurement --- VSharp.SILI.Core/Memory.fs | 17 +++-------- VSharp.SILI.Core/PathCondition.fs | 48 ++++++++++++++----------------- VSharp.Solver/Z3.fs | 13 ++------- VSharp.Test/IntegrationTests.cs | 8 ++---- 4 files changed, 30 insertions(+), 56 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index bcb8ac026..b687c65b2 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -510,15 +510,11 @@ module internal Memory = // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond let independentElsePc = keepDependentWith elsePc negatedCondition if thenPc.IsFalse then - Stopwatch.runMeasuringTime "then_is_trivial_false" (fun () -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - ) + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) elif elsePc.IsFalse then - Stopwatch.runMeasuringTime "else_is_trivial_false" (fun () -> - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - ) + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) else conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with @@ -527,17 +523,14 @@ module internal Memory = match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> - Stopwatch.runMeasuringTime "branch_1" id __insufficientInformation__ "Unable to witness branch" | SolverInteraction.SmtSat elseModel -> - Stopwatch.runMeasuringTime "branch_2" id conditionState.pc <- elsePc conditionState.model <- Some elseModel.mdl TaggedLogger.log conditionState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" TaggedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> - Stopwatch.runMeasuringTime "branch_3" id conditionState.pc <- elsePc TaggedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) @@ -546,13 +539,11 @@ module internal Memory = match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> - Stopwatch.runMeasuringTime "branch_4" id conditionState.pc <- thenPc TaggedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" TaggedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> - Stopwatch.runMeasuringTime "branch_5" id conditionState.model <- Some thenModel.mdl let thenState = conditionState let elseState = copy conditionState elsePc diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 373ac282b..ca7c85831 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -160,9 +160,7 @@ module public PC = Seq.map toString constraints |> Seq.sort |> join " /\ " member this.Copy() = - let inner() = - PathCondition(Dictionary(constants), HashSet(constraints), isFalse) - Stopwatch.runMeasuringTime "PC_Copy" inner + PathCondition(Dictionary(constants), HashSet(constraints), isFalse) member this.IsFalse = isFalse @@ -171,16 +169,14 @@ module public PC = member this.ToSeq() = seq constraints member this.Add newConstraint = - let inner() = - match newConstraint with - | True -> () - | False -> becomeTrivialFalse() - | _ when isFalse -> () - | _ when constraints.Contains(newConstraint) -> () - // what if constraint is not equal to newConstraint structurally, but is equal logically? - | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() - | _ -> addNewConstraintWithMerge newConstraint - Stopwatch.runMeasuringTime "PC_Add" inner + match newConstraint with + | True -> () + | False -> becomeTrivialFalse() + | _ when isFalse -> () + | _ when constraints.Contains(newConstraint) -> () + // what if constraint is not equal to newConstraint structurally, but is equal logically? + | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() + | _ -> addNewConstraintWithMerge newConstraint member this.Map mapper = let mapped = PathCondition() @@ -197,20 +193,18 @@ module public PC = /// one path condition are independent with constants contained in another one /// member this.Fragments = - let inner() = - if isFalse then - Seq.singleton this - else - let getSubsetByRepresentative = - function - | Tail(representative, constraints) -> - let constants = Dictionary() - addSubset constants (subset representative) constraints - let constraints = HashSet(PersistentSet.toSeq constraints) - Some(PathCondition(constants, constraints, false)) - | _ -> None - Seq.choose getSubsetByRepresentative constants.Values - Stopwatch.runMeasuringTime "PC_Fragments" inner + if isFalse then + Seq.singleton this + else + let getSubsetByRepresentative = + function + | Tail(representative, constraints) -> + let constants = Dictionary() + addSubset constants (subset representative) constraints + let constraints = HashSet(PersistentSet.toSeq constraints) + Some(PathCondition(constants, constraints, false)) + | _ -> None + Seq.choose getSubsetByRepresentative constants.Values let public add newConstraint (pc : PathCondition) = let copy = pc.Copy() diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index f8d311297..151f60162 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -800,17 +800,12 @@ module internal Z3 = yield query.expr } |> Array.ofSeq // let pathAtoms = addSoftConstraints q.lvl - let result = - Stopwatch.runMeasuringTime "Z3_check_sat" (fun () -> - optCtx.Check assumptions - ) + let result = optCtx.Check assumptions match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model let updatedModel = {q.currentModel with state = {q.currentModel.state with model = q.currentModel.state.model}} - Stopwatch.runMeasuringTime "update_model_after_check" (fun () -> - builder.UpdateModel z3Model updatedModel - ) + builder.UpdateModel z3Model updatedModel // let usedPaths = // pathAtoms // |> Seq.filter (fun atom -> z3Model.Eval(atom, false).IsTrue) @@ -839,9 +834,7 @@ module internal Z3 = else let levelAtom = getLevelAtom lvl ctx.MkImplies(levelAtom, encoded) - Stopwatch.runMeasuringTime "Z3_assert" (fun () -> - optCtx.Assert(leveled) - ) + optCtx.Assert(leveled) member x.AddPath encCtx (p : path) = printLog Trace "SOLVER: [lvl %O] Asserting path:" p.lvl diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index de3dadd87..806ab4e55 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -108,12 +108,8 @@ private TestResult Explore(TestExecutionContext context) SILI explorer = new SILI(_options); UnitTests unitTests = new UnitTests(Directory.GetCurrentDirectory()); - Stopwatch.runMeasuringTime("total_interpretation", FuncConvert.FromAction(() => - { - explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, - _ => { }, e => throw e); - })); - + explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); + if (unitTests.UnitTestsCount == 0 && unitTests.ErrorsCount == 0 && explorer.Statistics.IncompleteStates.Count == 0) { throw new Exception("No states were obtained! Most probably this is bug."); From c33fa0b7da77a8c280a1071f37073e0a0c3e756b Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 22:43:43 +0300 Subject: [PATCH 49/88] [fix] Remove tagged logging --- VSharp.SILI.Core/Memory.fs | 10 ---------- VSharp.SILI/Interpreter.fs | 1 - VSharp.Utils/TaggedLogger.fs | 5 +++-- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index b687c65b2..bca40d271 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -527,12 +527,9 @@ module internal Memory = | SolverInteraction.SmtSat elseModel -> conditionState.pc <- elsePc conditionState.model <- Some elseModel.mdl - TaggedLogger.log conditionState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - TaggedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtUnsat _ -> conditionState.pc <- elsePc - TaggedLogger.log conditionState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat thenModel -> conditionState.pc <- independentElsePc @@ -540,8 +537,6 @@ module internal Memory = | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> conditionState.pc <- thenPc - TaggedLogger.log conditionState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - TaggedLogger.log conditionState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> conditionState.model <- Some thenModel.mdl @@ -549,11 +544,6 @@ module internal Memory = let elseState = copy conditionState elsePc elseState.model <- Some elseModel.mdl thenState.pc <- thenPc - TaggedLogger.copy thenState.id elseState.id - TaggedLogger.log thenState.id $"Model stack: %s{thenModel.mdl.state.stack.frames.ToString()}" - TaggedLogger.log elseState.id $"Model stack: %s{elseModel.mdl.state.stack.frames.ToString()}" - TaggedLogger.log thenState.id $"Branching on: %s{(independentThenPc |> PC.toSeq |> conjunction).ToString()}" - TaggedLogger.log elseState.id $"Branching on: %s{(independentElsePc |> PC.toSeq |> conjunction).ToString()}" execution thenState elseState condition k) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = diff --git a/VSharp.SILI/Interpreter.fs b/VSharp.SILI/Interpreter.fs index bf2843cec..85f76a149 100644 --- a/VSharp.SILI/Interpreter.fs +++ b/VSharp.SILI/Interpreter.fs @@ -1951,7 +1951,6 @@ type internal ILInterpreter(isConcolicMode : bool) as this = | Instruction(offset, m) -> if offset = 0 then Logger.printLogLazy Logger.Info "Starting to explore method %O" (lazy Reflection.getFullMethodName m) - TaggedLogger.log cilState.state.id $"In method: %O{Reflection.getFullMethodName m}" x.ExecuteInstruction m offset cilState |> k | Exit m -> exit m diff --git a/VSharp.Utils/TaggedLogger.fs b/VSharp.Utils/TaggedLogger.fs index 61cc2580f..0f0017d61 100644 --- a/VSharp.Utils/TaggedLogger.fs +++ b/VSharp.Utils/TaggedLogger.fs @@ -35,5 +35,6 @@ module TaggedLogger = /// Tag to save the logs with /// Path of the file to save let public saveLog tag path = - let log = logs.GetValueOrDefault(tag, StringBuilder()).ToString() - File.WriteAllText(path, log) + if logs.ContainsKey tag then + let log = logs[tag].ToString() + File.WriteAllText(path, log) From f5b625292d7fd1bef59498e1c4846e4ae4f9e49e Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 22:55:56 +0300 Subject: [PATCH 50/88] [style] Format fixes --- VSharp.SILI.Core/Memory.fs | 14 +++++++------- VSharp.SILI.Core/SolverInteraction.fs | 2 +- VSharp.Solver/Z3.fs | 5 ++--- VSharp.Utils/Logger.fs | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index bca40d271..1c26d54a1 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -154,7 +154,7 @@ module internal Memory = {object : term} interface IStatedSymbolicConstantSource with override x.SubTerms = Seq.empty - override x.Time = VectorTime.zero + override x.Time = VectorTime.zero override x.IndependentWith otherSource = match otherSource with | :? hashCodeSource as otherHashCodeSource -> @@ -490,8 +490,8 @@ module internal Memory = let guardedStatedApply f state term = guardedStatedApplyk (Cps.ret2 f) state term id let guardedStatedMap mapper state term = - commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id - + commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id + let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = let keepDependentWith (pc : PC.PathCondition) cond = pc.Fragments @@ -511,10 +511,10 @@ module internal Memory = let independentElsePc = keepDependentWith elsePc negatedCondition if thenPc.IsFalse then conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) + elseBranch conditionState (List.singleton >> k) elif elsePc.IsFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) else conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with @@ -545,7 +545,7 @@ module internal Memory = elseState.model <- Some elseModel.mdl thenState.pc <- thenPc execution thenState elseState condition k) - + let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k let statedConditionalExecutionWithMerge state conditionInvocation thenBranch elseBranch = diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index df6aebc7a..08c66ff7c 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -46,4 +46,4 @@ module public SolverInteraction = |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } | None -> SmtUnknown "" - \ No newline at end of file + \ No newline at end of file diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 151f60162..4d5d7f804 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -9,7 +9,6 @@ open VSharp.Core.SolverInteraction open Logger module internal Z3 = - // ------------------------------- Exceptions ------------------------------- type EncodingException(msg : string) = @@ -800,7 +799,7 @@ module internal Z3 = yield query.expr } |> Array.ofSeq // let pathAtoms = addSoftConstraints q.lvl - let result = optCtx.Check assumptions + let result = optCtx.Check assumptions match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model @@ -835,7 +834,7 @@ module internal Z3 = let levelAtom = getLevelAtom lvl ctx.MkImplies(levelAtom, encoded) optCtx.Assert(leveled) - + member x.AddPath encCtx (p : path) = printLog Trace "SOLVER: [lvl %O] Asserting path:" p.lvl printLogLazy Trace " %s" (lazy(PathConditionToSeq p.state.pc |> Seq.map toString |> join " /\\ \n ")) diff --git a/VSharp.Utils/Logger.fs b/VSharp.Utils/Logger.fs index 3887b653f..3f3b6809e 100644 --- a/VSharp.Utils/Logger.fs +++ b/VSharp.Utils/Logger.fs @@ -8,7 +8,7 @@ module Logger = let Info = 3 let Trace = 4 - let mutable current_log_level = Trace + let mutable current_log_level = Error let mutable current_text_writer = Console.Out let public ConfigureWriter writer = current_text_writer <- writer From 545e56c1d543548260f933d6617b674e803f949e Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 22:57:21 +0300 Subject: [PATCH 51/88] [fix] Remove changes in regex test --- VSharp.Test/Tests/RegExTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VSharp.Test/Tests/RegExTest.cs b/VSharp.Test/Tests/RegExTest.cs index cb310c834..d280823d3 100644 --- a/VSharp.Test/Tests/RegExTest.cs +++ b/VSharp.Test/Tests/RegExTest.cs @@ -51,9 +51,9 @@ public static bool Match(string re, string text) public class RegExTest { [TestSvm(100)] - public static string OwnImplementationTest111(char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8, char c9, char c10) + public static string OwnImplementationTest(char c1, char c2, char c3, char c4, char c5, char c6) { - string pattern = new string(new char[] {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10}); + string pattern = new string(new char[] {c1, c2, c3}); string result = ""; if (RegExImplementation.Match(pattern, "hello")) { @@ -108,7 +108,7 @@ public static bool OwnImplementationTest4(string pattern) return RegExImplementation.Match(pattern, "Hello"); } - //[TestSvm(100)] + // [TestSvm(100)] [Ignore("need more external method implementations")] public static MatchCollection SmallSystemImplementationTest() { From 62d99e3a788fe5b121b453bfd9a40b6518bc5bb4 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 20 Mar 2022 23:01:01 +0300 Subject: [PATCH 52/88] [style] One more format fix --- VSharp.SILI.Core/SolverInteraction.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 08c66ff7c..84083e0ef 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -46,4 +46,4 @@ module public SolverInteraction = |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } | None -> SmtUnknown "" - \ No newline at end of file + \ No newline at end of file From e6981597b99c8a25755e4cc31dd962de8c7623cd Mon Sep 17 00:00:00 2001 From: mxprshn Date: Wed, 13 Apr 2022 00:39:06 +0300 Subject: [PATCH 53/88] [feat] First incrementality attempt --- VSharp.SILI.Core/Memory.fs | 2 ++ VSharp.SILI.Core/SolverInteraction.fs | 15 +++++++++++++- VSharp.Solver/Z3.fs | 28 +++++++++++++++++++++++++++ VSharp.Utils/Logger.fs | 2 +- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index d782dc90a..93031969b 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -469,6 +469,8 @@ module internal Memory = elseBranch elseState (fun elseResult -> merge2Results thenResult elseResult |> k)) conditionInvocation state (fun (condition, conditionState) -> + SolverInteraction.assertAssumption state condition + SolverInteraction.assertAssumption state (!!condition) let thenPc = PC.add state.pc condition let elsePc = PC.add state.pc (!!condition) if PC.isFalse thenPc then diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index ec9e8d9a9..33b6e7e21 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -22,6 +22,8 @@ module public SolverInteraction = abstract CheckSat : encodingContext -> query -> smtResult abstract Assert : encodingContext -> level -> formula -> unit abstract AddPath : encodingContext -> path -> unit + abstract AssertAssumption : encodingContext -> string -> formula -> unit + abstract CheckAssumptions : string seq -> smtResult let mutable private solver : ISolver option = None @@ -35,8 +37,19 @@ module public SolverInteraction = { addressOrder = orderWithNull } let checkSat state = // TODO: need to solve types here? #do - let ctx = getEncodingContext state +(* let ctx = getEncodingContext state let formula = PC.toSeq state.pc |> conjunction match solver with | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } + | None -> SmtUnknown ""*) + let names = PC.toSeq state.pc |> Seq.map (fun t -> t.GetHashCode().ToString()) + match solver with + | Some s -> s.CheckAssumptions names | None -> SmtUnknown "" + + let assertAssumption state condition = + let ctx = getEncodingContext state + let name = condition.GetHashCode().ToString() + match solver with + | Some s -> s.AssertAssumption ctx name condition + | None -> () diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index b5539aea2..4afe00ff2 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -743,11 +743,13 @@ module internal Z3 = type internal Z3Solver() = // let optCtx = ctx.MkOptimize() + // Why Solver is named optCtx? let optCtx = ctx.MkSolver() let levelAtoms = List() let mutable pathsCount = 0u let pathAtoms = Dictionary>() let paths = Dictionary() + let assumptionNames = HashSet() let getLevelAtom (lvl : level) = assert (not <| Level.isInf lvl) @@ -839,6 +841,32 @@ module internal Z3 = let encodedWithAssumptions = Seq.append assumptions encoded |> Array.ofSeq let encoded = builder.MkAnd encodedWithAssumptions optCtx.Assert(ctx.MkImplies(pathAtom, encoded)) + + member x.AssertAssumption encCtx name formula = + if (assumptionNames.Contains name |> not) then + printLog Trace $"SOLVER: assert: %s{name} => %s{formula.ToString()}" + assumptionNames.Add name |> ignore + let from = ctx.MkBoolConst name + let encoded = builder.EncodeTerm encCtx formula + let implication = ctx.MkImplies (from, encoded.expr :?> BoolExpr) + optCtx.Assert implication + + member x.CheckAssumptions names = + let assumptions = names |> Seq.distinct |> Seq.map ctx.MkBoolConst + let amp = " & " + printLog Trace $"SOLVER: check: %s{join amp names}" + let result = optCtx.Check assumptions + match result with + | Status.SATISFIABLE -> + let z3Model = optCtx.Model + let model = builder.MkModel z3Model + SmtSat { mdl = model; usedPaths = [] } + | Status.UNSATISFIABLE -> + SmtUnsat { core = Array.empty } + | Status.UNKNOWN -> + SmtUnknown optCtx.ReasonUnknown + | _ -> __unreachable__() + let reset() = builder.Reset() diff --git a/VSharp.Utils/Logger.fs b/VSharp.Utils/Logger.fs index 3f3b6809e..3887b653f 100644 --- a/VSharp.Utils/Logger.fs +++ b/VSharp.Utils/Logger.fs @@ -8,7 +8,7 @@ module Logger = let Info = 3 let Trace = 4 - let mutable current_log_level = Error + let mutable current_log_level = Trace let mutable current_text_writer = Console.Out let public ConfigureWriter writer = current_text_writer <- writer From c2b15d88c2e5d6df39201be4628c8d005ce6bfb5 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 19 Apr 2022 00:04:56 +0300 Subject: [PATCH 54/88] [fix] Pass stack trace to test result for debugging --- VSharp.Test/IntegrationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 129b4c868..f01756a43 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -135,7 +135,7 @@ private TestResult Explore(TestExecutionContext context) } catch (Exception e) { - context.CurrentResult.SetResult(ResultState.Error, e.Message); + context.CurrentResult.SetResult(ResultState.Error, e.Message, e.StackTrace); } return context.CurrentResult; From 96d1dacbf093224a72488d07341f425925ed6fe3 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sat, 30 Apr 2022 12:34:19 +0300 Subject: [PATCH 55/88] Very dirty attempt to eval model before --- VSharp.SILI.Core/Memory.fs | 77 ++++++++++++++++++++++++--------- VSharp.Test/IntegrationTests.cs | 2 +- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 1c26d54a1..f69dc19bc 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -5,6 +5,7 @@ open System.Collections.Generic open System.Text open FSharpx.Collections open VSharp +open VSharp.Core open VSharp.Core.Types open VSharp.Core.Types.Constructor open VSharp.Utils @@ -516,35 +517,69 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - conditionState.pc <- independentThenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with + let pcConstants = state.pc.ToSeq() |> discoverConstants + let condConstants = condition |> Seq.singleton |> discoverConstants + let noNewConstants = pcConstants.IsSupersetOf condConstants + let kek = state.model.Value.Eval condition + if (noNewConstants && (isTrue kek)) then + Logger.trace $"Current model satisfies {condition}" + let elseState = copy conditionState independentElsePc + conditionState.pc <- thenPc + conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model + match SolverInteraction.checkSat elseState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" + thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- elsePc - conditionState.model <- Some elseModel.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> + elseState.pc <- elsePc + elseState.model <- Some elseModel.mdl + execution conditionState elseState condition k + elif (noNewConstants && (isFalse kek)) then + Logger.trace $"Current model satisfies {negatedCondition}" + let thenState = copy conditionState independentThenPc conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with + conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model + match SolverInteraction.checkSat thenState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + let elseState = copy conditionState elsePc conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState elsePc - elseState.model <- Some elseModel.mdl - thenState.pc <- thenPc - execution thenState elseState condition k) + execution conditionState elseState condition k + else + Logger.trace "Current model doesn't satisfy condition or its negation" + conditionState.pc <- independentThenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.model <- Some thenModel.mdl + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState elsePc + elseState.model <- Some elseModel.mdl + thenState.pc <- thenPc + execution thenState elseState condition k) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 806ab4e55..924b32c2f 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -139,7 +139,7 @@ private TestResult Explore(TestExecutionContext context) } catch (Exception e) { - context.CurrentResult.SetResult(ResultState.Error, e.Message); + context.CurrentResult.SetResult(ResultState.Error, e.Message, e.StackTrace); } return context.CurrentResult; From 58edff084f28eac1919bb7e23020dcfe59e53e66 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Fri, 6 May 2022 23:25:07 +0300 Subject: [PATCH 56/88] [fix] Assert new assumptions right before check sat --- VSharp.SILI.Core/Memory.fs | 2 -- VSharp.SILI.Core/SolverInteraction.fs | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 93031969b..d782dc90a 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -469,8 +469,6 @@ module internal Memory = elseBranch elseState (fun elseResult -> merge2Results thenResult elseResult |> k)) conditionInvocation state (fun (condition, conditionState) -> - SolverInteraction.assertAssumption state condition - SolverInteraction.assertAssumption state (!!condition) let thenPc = PC.add state.pc condition let elsePc = PC.add state.pc (!!condition) if PC.isFalse thenPc then diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 33b6e7e21..d4134ac27 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -42,14 +42,10 @@ module public SolverInteraction = match solver with | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } | None -> SmtUnknown ""*) - let names = PC.toSeq state.pc |> Seq.map (fun t -> t.GetHashCode().ToString()) - match solver with - | Some s -> s.CheckAssumptions names - | None -> SmtUnknown "" - - let assertAssumption state condition = let ctx = getEncodingContext state - let name = condition.GetHashCode().ToString() + let conditionsWithHashCodes = PC.toSeq state.pc |> Seq.map (fun t -> t, t.GetHashCode().ToString()) match solver with - | Some s -> s.AssertAssumption ctx name condition - | None -> () + | Some s -> + conditionsWithHashCodes |> Seq.iter (fun (t, hc) -> s.AssertAssumption ctx hc t) + conditionsWithHashCodes |> Seq.map snd |> s.CheckAssumptions + | None -> SmtUnknown "" From 73430cf0950efe543e87a6200bd11bcc3a73540b Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sat, 7 May 2022 11:12:22 +0300 Subject: [PATCH 57/88] [fix] Don't forget to assert additional assumptions --- VSharp.Solver/Z3.fs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 4afe00ff2..ca9f838ad 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -848,9 +848,15 @@ module internal Z3 = assumptionNames.Add name |> ignore let from = ctx.MkBoolConst name let encoded = builder.EncodeTerm encCtx formula - let implication = ctx.MkImplies (from, encoded.expr :?> BoolExpr) - optCtx.Assert implication - + seq { + yield! Seq.cast encoded.assumptions + yield encoded.expr + } + |> Seq.iter (fun (implies: Expr) -> + let implication = ctx.MkImplies (from, implies :?> BoolExpr) + optCtx.Assert implication + ) + member x.CheckAssumptions names = let assumptions = names |> Seq.distinct |> Seq.map ctx.MkBoolConst let amp = " & " @@ -866,7 +872,6 @@ module internal Z3 = | Status.UNKNOWN -> SmtUnknown optCtx.ReasonUnknown | _ -> __unreachable__() - let reset() = builder.Reset() From f90e040a2f3c372710021755db1315dd1d893783 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sat, 7 May 2022 15:30:02 +0300 Subject: [PATCH 58/88] [fix] Catch double encoding exceptions --- VSharp.SILI.Core/SolverInteraction.fs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index d4134ac27..d6bb37460 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -46,6 +46,9 @@ module public SolverInteraction = let conditionsWithHashCodes = PC.toSeq state.pc |> Seq.map (fun t -> t, t.GetHashCode().ToString()) match solver with | Some s -> - conditionsWithHashCodes |> Seq.iter (fun (t, hc) -> s.AssertAssumption ctx hc t) - conditionsWithHashCodes |> Seq.map snd |> s.CheckAssumptions + try + conditionsWithHashCodes |> Seq.iter (fun (t, hc) -> s.AssertAssumption ctx hc t) + conditionsWithHashCodes |> Seq.map snd |> s.CheckAssumptions + with + | e -> SmtUnknown $"Solver couldn't assert and check assumptions: %s{e.Message}" | None -> SmtUnknown "" From e9cb57fbb31bacc4bd8c1669b072db6ccde5f372 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 10 May 2022 11:20:24 +0300 Subject: [PATCH 59/88] [style] Prettify code a bit --- VSharp.SILI.Core/Memory.fs | 17 +++++++---------- VSharp.SILI.Core/PathCondition.fs | 5 +++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index f69dc19bc..a5daaf477 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -517,15 +517,14 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - let pcConstants = state.pc.ToSeq() |> discoverConstants + let pcConstants = HashSet(state.pc.Constants) let condConstants = condition |> Seq.singleton |> discoverConstants let noNewConstants = pcConstants.IsSupersetOf condConstants - let kek = state.model.Value.Eval condition - if (noNewConstants && (isTrue kek)) then - Logger.trace $"Current model satisfies {condition}" + let evaluatedCondition = state.model.Value.Eval condition + // Current model satisfies new condition, so we can keep it for 'then' branch + if (noNewConstants && (isTrue evaluatedCondition)) then let elseState = copy conditionState independentElsePc conditionState.pc <- thenPc - conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model match SolverInteraction.checkSat elseState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> @@ -534,14 +533,13 @@ module internal Memory = elseState.pc <- elsePc elseState.model <- Some elseModel.mdl execution conditionState elseState condition k - elif (noNewConstants && (isFalse kek)) then - Logger.trace $"Current model satisfies {negatedCondition}" + // Current model satisfies !condition, so we can keep it for 'else' branch + elif (noNewConstants && (isFalse evaluatedCondition)) then let thenState = copy conditionState independentThenPc - conditionState.pc <- elsePc - conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model match SolverInteraction.checkSat thenState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat thenModel -> let elseState = copy conditionState elsePc @@ -549,7 +547,6 @@ module internal Memory = conditionState.model <- Some thenModel.mdl execution conditionState elseState condition k else - Logger.trace "Current model doesn't satisfy condition or its negation" conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnknown _ -> diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index ca7c85831..defa96241 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -205,6 +205,11 @@ module public PC = Some(PathCondition(constants, constraints, false)) | _ -> None Seq.choose getSubsetByRepresentative constants.Values + + /// + /// Returns all constants contained in path condition + /// + member this.Constants = seq constants.Keys let public add newConstraint (pc : PathCondition) = let copy = pc.Copy() From b2081550a3edbe18a13f320a4ed13161003cc27e Mon Sep 17 00:00:00 2001 From: mxprshn Date: Wed, 11 May 2022 23:33:56 +0300 Subject: [PATCH 60/88] [feat] Add conjunction splitting --- VSharp.SILI.Core/Propositional.fs | 6 ++++++ VSharp.SILI.Core/SolverInteraction.fs | 22 +++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/VSharp.SILI.Core/Propositional.fs b/VSharp.SILI.Core/Propositional.fs index da42e3e0e..d284b3738 100644 --- a/VSharp.SILI.Core/Propositional.fs +++ b/VSharp.SILI.Core/Propositional.fs @@ -1,6 +1,7 @@ namespace VSharp.Core open VSharp +open VSharp.Core type IPropositionalSimplifier = abstract member Simplify : term -> term @@ -245,6 +246,11 @@ module internal Propositional = if Seq.isEmpty xs then x else Seq.fold (&&&) x xs | _ -> True + + let splitConjunction term = + match term.term with + | Conjunction args -> List.toSeq args + | _ -> Seq.singleton term let disjunction = function | Seq.Cons(x, xs) -> diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index d6bb37460..48a7c9c61 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -35,20 +35,28 @@ module public SolverInteraction = let order = Seq.fold (fun (map, i) address -> Map.add address i map, i + 1) (Map.empty, 1) sortedAddresses |> fst let orderWithNull = Map.add VectorTime.zero 0 order { addressOrder = orderWithNull } - - let checkSat state = // TODO: need to solve types here? #do -(* let ctx = getEncodingContext state + + let private checkSatPlainly state = + let ctx = getEncodingContext state let formula = PC.toSeq state.pc |> conjunction match solver with | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } - | None -> SmtUnknown ""*) + | None -> SmtUnknown "" + + let private checkSatIncrementally split naming state = let ctx = getEncodingContext state - let conditionsWithHashCodes = PC.toSeq state.pc |> Seq.map (fun t -> t, t.GetHashCode().ToString()) + let conditionsWithNames = + PC.toSeq state.pc + |> split + |> Seq.map (fun t -> t, naming t) match solver with | Some s -> try - conditionsWithHashCodes |> Seq.iter (fun (t, hc) -> s.AssertAssumption ctx hc t) - conditionsWithHashCodes |> Seq.map snd |> s.CheckAssumptions + conditionsWithNames |> Seq.iter (fun (t, hc) -> s.AssertAssumption ctx hc t) + conditionsWithNames |> Seq.map snd |> s.CheckAssumptions with | e -> SmtUnknown $"Solver couldn't assert and check assumptions: %s{e.Message}" | None -> SmtUnknown "" + + let checkSat = // TODO: need to solve types here? #do + checkSatIncrementally (Seq.collect splitConjunction) (fun t -> t.GetHashCode().ToString()) From 56986185339f41796024dcca1d7e452135f7f9ad Mon Sep 17 00:00:00 2001 From: mxprshn Date: Thu, 12 May 2022 22:03:31 +0300 Subject: [PATCH 61/88] [fix] Move assertion and check to one function for efficiency --- VSharp.SILI.Core/SolverInteraction.fs | 15 +++------ VSharp.Solver/Z3.fs | 45 ++++++++++++++++----------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 48a7c9c61..8142f2902 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -22,8 +22,7 @@ module public SolverInteraction = abstract CheckSat : encodingContext -> query -> smtResult abstract Assert : encodingContext -> level -> formula -> unit abstract AddPath : encodingContext -> path -> unit - abstract AssertAssumption : encodingContext -> string -> formula -> unit - abstract CheckAssumptions : string seq -> smtResult + abstract CheckAssumptions : encodingContext -> formula seq -> smtResult let mutable private solver : ISolver option = None @@ -43,20 +42,16 @@ module public SolverInteraction = | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } | None -> SmtUnknown "" - let private checkSatIncrementally split naming state = + let private checkSatIncrementally state = let ctx = getEncodingContext state - let conditionsWithNames = - PC.toSeq state.pc - |> split - |> Seq.map (fun t -> t, naming t) + let conditions = state.pc |> PC.toSeq match solver with | Some s -> try - conditionsWithNames |> Seq.iter (fun (t, hc) -> s.AssertAssumption ctx hc t) - conditionsWithNames |> Seq.map snd |> s.CheckAssumptions + s.CheckAssumptions ctx conditions with | e -> SmtUnknown $"Solver couldn't assert and check assumptions: %s{e.Message}" | None -> SmtUnknown "" let checkSat = // TODO: need to solve types here? #do - checkSatIncrementally (Seq.collect splitConjunction) (fun t -> t.GetHashCode().ToString()) + checkSatIncrementally diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index ca9f838ad..38b948e0a 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -749,7 +749,8 @@ module internal Z3 = let mutable pathsCount = 0u let pathAtoms = Dictionary>() let paths = Dictionary() - let assumptionNames = HashSet() + + let assumptions = Dictionary() let getLevelAtom (lvl : level) = assert (not <| Level.isInf lvl) @@ -841,27 +842,33 @@ module internal Z3 = let encodedWithAssumptions = Seq.append assumptions encoded |> Array.ofSeq let encoded = builder.MkAnd encodedWithAssumptions optCtx.Assert(ctx.MkImplies(pathAtom, encoded)) - - member x.AssertAssumption encCtx name formula = - if (assumptionNames.Contains name |> not) then - printLog Trace $"SOLVER: assert: %s{name} => %s{formula.ToString()}" - assumptionNames.Add name |> ignore - let from = ctx.MkBoolConst name + + member x.CheckAssumptions encCtx formulas = + let encodeToBoolExprs formula = let encoded = builder.EncodeTerm encCtx formula seq { - yield! Seq.cast encoded.assumptions - yield encoded.expr - } - |> Seq.iter (fun (implies: Expr) -> - let implication = ctx.MkImplies (from, implies :?> BoolExpr) - optCtx.Assert implication - ) - - member x.CheckAssumptions names = - let assumptions = names |> Seq.distinct |> Seq.map ctx.MkBoolConst + yield! Seq.cast encoded.assumptions + yield encoded.expr :?> BoolExpr + } + let exprs = Seq.collect encodeToBoolExprs formulas + let boolConsts = seq { + for expr in exprs do + let mutable name = "" + if (assumptions.TryGetValue(expr, &name)) then + yield ctx.MkBoolConst name + else + name <- Guid.NewGuid().ToString() + printLog Trace $"SOLVER: assert: %s{name} => {expr}" + assumptions.[expr] <- name + let boolConst = ctx.MkBoolConst name + let implication = ctx.MkImplies(boolConst, expr) + optCtx.Assert implication + yield boolConst + } + let names = boolConsts |> Seq.map (fun c -> c.ToString()) let amp = " & " - printLog Trace $"SOLVER: check: %s{join amp names}" - let result = optCtx.Check assumptions + printLog Trace $"SOLVER: check: {join amp names}" + let result = optCtx.Check boolConsts match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model From 220bb8b66bb13b0de8f954ce52be91fae3117aa5 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Thu, 12 May 2022 23:09:03 +0300 Subject: [PATCH 62/88] [fix] Use ints as names for assumptions --- VSharp.SILI.Core/SolverInteraction.fs | 10 ++--- VSharp.Solver/Z3.fs | 63 +++++++++++++++------------ 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 8142f2902..57d13ad2f 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -40,18 +40,14 @@ module public SolverInteraction = let formula = PC.toSeq state.pc |> conjunction match solver with | Some s -> s.CheckSat ctx {lvl = Level.zero; queryFml = formula } - | None -> SmtUnknown "" + | None -> SmtUnknown "Solver not configured" let private checkSatIncrementally state = let ctx = getEncodingContext state let conditions = state.pc |> PC.toSeq match solver with - | Some s -> - try - s.CheckAssumptions ctx conditions - with - | e -> SmtUnknown $"Solver couldn't assert and check assumptions: %s{e.Message}" - | None -> SmtUnknown "" + | Some s -> s.CheckAssumptions ctx conditions + | None -> SmtUnknown "Solver not configured" let checkSat = // TODO: need to solve types here? #do checkSatIncrementally diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 38b948e0a..c69dc92b5 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -850,35 +850,40 @@ module internal Z3 = yield! Seq.cast encoded.assumptions yield encoded.expr :?> BoolExpr } - let exprs = Seq.collect encodeToBoolExprs formulas - let boolConsts = seq { - for expr in exprs do - let mutable name = "" - if (assumptions.TryGetValue(expr, &name)) then - yield ctx.MkBoolConst name - else - name <- Guid.NewGuid().ToString() - printLog Trace $"SOLVER: assert: %s{name} => {expr}" - assumptions.[expr] <- name - let boolConst = ctx.MkBoolConst name - let implication = ctx.MkImplies(boolConst, expr) - optCtx.Assert implication - yield boolConst - } - let names = boolConsts |> Seq.map (fun c -> c.ToString()) - let amp = " & " - printLog Trace $"SOLVER: check: {join amp names}" - let result = optCtx.Check boolConsts - match result with - | Status.SATISFIABLE -> - let z3Model = optCtx.Model - let model = builder.MkModel z3Model - SmtSat { mdl = model; usedPaths = [] } - | Status.UNSATISFIABLE -> - SmtUnsat { core = Array.empty } - | Status.UNKNOWN -> - SmtUnknown optCtx.ReasonUnknown - | _ -> __unreachable__() + try + let exprs = Seq.collect encodeToBoolExprs formulas + let boolConsts = seq { + for expr in exprs do + let mutable name = "" + if (assumptions.TryGetValue(expr, &name)) then + yield ctx.MkBoolConst name + else + name <- $"p{assumptions.Count}" + printLog Trace $"SOLVER: assert: {name} => {expr}" + assumptions.[expr] <- name + let boolConst = ctx.MkBoolConst name + let implication = ctx.MkImplies(boolConst, expr) + optCtx.Assert implication + yield boolConst + } + let names = boolConsts |> Seq.map (fun c -> c.ToString()) + let amp = " & " + printLog Trace $"SOLVER: check: {join amp names}" + let result = optCtx.Check boolConsts + match result with + | Status.SATISFIABLE -> + let z3Model = optCtx.Model + let model = builder.MkModel z3Model + SmtSat { mdl = model; usedPaths = [] } + | Status.UNSATISFIABLE -> + SmtUnsat { core = Array.empty } + | Status.UNKNOWN -> + SmtUnknown optCtx.ReasonUnknown + | _ -> __unreachable__() + with + | :? EncodingException as e -> + printLog Info "SOLVER: exception was thrown: %s" e.Message + SmtUnknown (sprintf "Z3 has thrown an exception: %s" e.Message) let reset() = builder.Reset() From 97afb914d1cdd35b54e805fdaf6e2ddd6816ebda Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 12:35:03 +0300 Subject: [PATCH 63/88] [fix] Fix incrementality and independence merge conflicts --- VSharp.SILI.Core/SolverInteraction.fs | 13 +++++++++---- VSharp.Solver/Z3.fs | 10 +++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 1f8db9ef4..e382a83dc 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -24,7 +24,7 @@ module public SolverInteraction = abstract CheckSat : encodingContext -> query -> smtResult abstract Assert : encodingContext -> level -> formula -> unit abstract AddPath : encodingContext -> path -> unit - abstract CheckAssumptions : encodingContext -> formula seq -> smtResult + abstract CheckAssumptions : encodingContext -> model -> formula seq -> smtResult let mutable private solver : ISolver option = None @@ -46,13 +46,18 @@ module public SolverInteraction = state.model |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } - | None -> SmtUnknown "" + | None -> SmtUnknown "Solver not configured" let private checkSatIncrementally state = let ctx = getEncodingContext state let conditions = state.pc |> PC.toSeq match solver with - | Some s -> s.CheckAssumptions ctx conditions + | Some s -> + let model = + state.model + |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } + s.CheckAssumptions ctx model conditions | None -> SmtUnknown "Solver not configured" - let checkSat = // TODO: need to solve types here? #do \ No newline at end of file + let checkSat = + checkSatIncrementally// TODO: need to solve types here? #do \ No newline at end of file diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index edcf34f4f..941d61c6f 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -847,7 +847,7 @@ module internal Z3 = let encoded = builder.MkAnd encodedWithAssumptions optCtx.Assert(ctx.MkImplies(pathAtom, encoded)) - member x.CheckAssumptions encCtx formulas = + member x.CheckAssumptions encCtx currentModel formulas = let encodeToBoolExprs formula = let encoded = builder.EncodeTerm encCtx formula seq { @@ -877,11 +877,15 @@ module internal Z3 = match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model - let model = builder.MkModel z3Model - SmtSat { mdl = model; usedPaths = [] } + let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} + builder.UpdateModel z3Model updatedModel + builder.ClearT2E() + SmtSat { mdl = updatedModel; usedPaths = [] } | Status.UNSATISFIABLE -> + builder.ClearT2E() SmtUnsat { core = Array.empty } | Status.UNKNOWN -> + builder.ClearT2E() SmtUnknown optCtx.ReasonUnknown | _ -> __unreachable__() with From 1ebc1384d96be31c9f039909b5f7422d99b3710b Mon Sep 17 00:00:00 2001 From: mxprshn Date: Mon, 16 May 2022 11:57:09 +0300 Subject: [PATCH 64/88] Add some tests with 'logic bombs' --- VSharp.Test/Tests/Recursion.cs | 14 +- VSharp.Test/Tests/SolverBenchmark.cs | 325 +++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 7 deletions(-) create mode 100644 VSharp.Test/Tests/SolverBenchmark.cs diff --git a/VSharp.Test/Tests/Recursion.cs b/VSharp.Test/Tests/Recursion.cs index b1809ca81..d6b78fe84 100644 --- a/VSharp.Test/Tests/Recursion.cs +++ b/VSharp.Test/Tests/Recursion.cs @@ -14,13 +14,13 @@ private static int FibRec(int n) return FibRec(n - 1) + FibRec(n - 2); } - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static int Fib2() { return FibRec(2); } - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static int Fib5() { return FibRec(5); @@ -46,7 +46,7 @@ private static void MutatingFib(int n) } } - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static int FibUnbound(int n) { _c = 42; @@ -68,13 +68,13 @@ private static int GcdRec(int n, int m) return GcdRec(n, m - n); } - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static int Gcd1() { return GcdRec(15, 4); } - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static int Gcd15() { return GcdRec(30, 75); @@ -84,7 +84,7 @@ public static int Gcd15() [TestSvmFixture] public static class McCarthy91 { - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static int McCarthy(int n) { return n > 100 ? n - 10 : McCarthy(McCarthy(n + 11)); @@ -135,7 +135,7 @@ private static BigClass MutateAfterRecursion(BigClass s, int n) return s; } - [Ignore("Forward exploration does not handle recursion now")] + [TestSvm] public static SmallClass MutationAfterRecursionTest(int n) { var s1 = new BigClass {Small = new SmallClass()}; diff --git a/VSharp.Test/Tests/SolverBenchmark.cs b/VSharp.Test/Tests/SolverBenchmark.cs new file mode 100644 index 000000000..75a615377 --- /dev/null +++ b/VSharp.Test/Tests/SolverBenchmark.cs @@ -0,0 +1,325 @@ +using System; +using NUnit.Framework; +using VSharp.Test; + +namespace IntegrationTests +{ + [TestSvmFixture] + public static class SolverBenchmark + { + /* + * Idea with Collatz conjecture is taken from + * 'On Benchmarking the Capability of Symbolic Execution Tools with Logic Bombs' + * by Hui Xu et al. + */ + private static int CollatzStep(int a) + { + if (a % 2 == 0) + { + return a / 2; + } + + return 3 * a + 1; + } + + [TestSvm] + public static int CollatzLogicBomOmb1(int i) + { + if (i <= 0) + { + return 0; + } + + var loopCount = 0; + var j = i; + + while (j != 1) + { + j = CollatzStep(j); + ++loopCount; + } + + if (loopCount % 2 == 0) + { + return 1; + } + + return 2; + } + + [TestSvm] + public static int CollatzLogicBomOmb2(int i) + { + if (i <= 0 || i >= 100) + { + return 0; + } + + var loopCount = 0; + var j = i; + + while (j != 1) + { + j = CollatzStep(j); + ++loopCount; + } + + // Fails with 17 + if (loopCount == 9) + { + return 1; + } + + return 2; + } + + private static int CustomCollatzStep(int a, int b) + { + if (a % 2 == 0) + { + return a / 2; + } + + return b * a + 1; + } + + // Fails with j == 11 and loopCount == 14 + [TestSvm] + public static int CollatzLogicBomOmb3(int i) + { + var j = 12; + var loopCount = 0; + + while (j != 1) + { + j = CustomCollatzStep(j, i); + ++loopCount; + } + + if (loopCount == 9) + { + return 1; + } + + return 2; + } + + [TestSvm] + public static int CollatzLogicBomOmb4(int i, int j) + { + var loopCount = 0; + + while (i != 1) + { + if (j == 1) + { + break; + } + + i = CollatzStep(i); + j = CollatzStep(j); + + ++loopCount; + } + + if (loopCount > 3) + { + return 1; + } + + return 2; + } + + [TestSvm] + public static int CollatzLogicBomOmb5(int i, int j, int k) + { + var loopCount = 0; + + while (i != 1) + { + if (j == 1) + { + break; + } + + if (k == 1) + { + break; + } + + i = CollatzStep(i); + j = CollatzStep(j); + k = CollatzStep(k); + + ++loopCount; + } + + if (loopCount > 2) + { + return 1; + } + + return 2; + } + + [TestSvm] + public static int CollatzLogicBomOmb6(int i, int j) + { + var loopCount = 0; + + while (i + j != 2) + { + i = CollatzStep(i); + j = CollatzStep(j); + + ++loopCount; + } + + if (loopCount > 3) + { + return 1; + } + + return 2; + } + + [TestSvm] + public static int CollatzLogicBomOmb7(int i) + { + for (var j = 1; j < 100; ++j) + { + var k = j; + + while (k != i) + { + k = CollatzStep(k); + } + } + + return 0; + } + + [TestSvm] + public static int CollatzLogicBomOmb8(int i, int j) + { + for (var k = 1; k < 17; ++k) + { + var l = k; + + while (l != i) + { + l = CustomCollatzStep(l, j); + } + } + + return 0; + } + + private static int[,] sudoku = + { + { 0, 2, 0, 0 }, + { 3, 0, 0, 0 }, + { 2, 0, 0, 0 }, + { 0, 3, 0, 4 } + }; + + [Ignore("Fails with 'key not found' in getOpCode")] + public static bool SudokuSolution(int[] values) + { + for (var i = 0; i < values.Length; ++i) + { + if (values[i] < 1 || values[i] > 4) + { + return false; + } + } + + var zeroCount = 0; + + for (var i = 0; i < 4; ++i) + { + for (var j = 0; j < 4; ++j) + { + if (sudoku[i, j] == 0) + { + ++zeroCount; + } + } + } + + if (zeroCount != values.Length) + { + return false; + } + + var currentZero = 0; + + for (var i = 0; i < 4; ++i) + { + for (var j = 0; j < 4; ++j) + { + if (sudoku[i, j] == 0) + { + sudoku[i, j] = values[currentZero]; + ++currentZero; + } + } + } + + for (var i = 0; i < 4; ++i) + { + var filled = new bool[4]; + for (var j = 0; j < 4; ++j) + { + filled[sudoku[i, j] - 1] = true; + } + for (var j = 0; j < 4; ++j) + { + if (!filled[j]) + { + return false; + } + } + } + + for (var i = 0; i < 4; ++i) + { + var filled = new bool[4]; + for (var j = 0; j < 4; ++j) + { + filled[sudoku[j, i] - 1] = true; + } + for (var j = 0; j < 4; ++j) + { + if (!filled[j]) + { + return false; + } + } + } + + for (var i = 0; i < 4; i += 2) + { + for (var j = 0; j < 4; j += 2) + { + var filled = new bool[4]; + + for (var k = 0; k < 2; ++k) + { + for (var l = 0; l < 2; ++l) + { + filled[sudoku[i + k, j + l] - 1] = true; + } + } + + if (!filled[j]) + { + return false; + } + } + } + + return true; + } + } +} From 01d8c3801a23cac013b25bc3800d341b150db635 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 12:54:21 +0300 Subject: [PATCH 65/88] [fix] Fix not updated model --- VSharp.SILI.Core/Memory.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 1c26d54a1..5fe67dcaf 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -536,6 +536,7 @@ module internal Memory = match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> + conditionState.model <- Some thenModel.mdl conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> From 01a6d344e8cf98f03adc206611abad2f4defa26f Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 12:59:22 +0300 Subject: [PATCH 66/88] Revert "Merge branch 'eval-model-on-branching' into optimizations" This reverts commit 39053412720e2180ab04b007e19527d2481e7e2e. --- VSharp.SILI.Core/Memory.fs | 40 ++++++++++--------------------- VSharp.SILI.Core/PathCondition.fs | 5 ---- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 3378dc9c3..5fe67dcaf 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -5,7 +5,6 @@ open System.Collections.Generic open System.Text open FSharpx.Collections open VSharp -open VSharp.Core open VSharp.Core.Types open VSharp.Core.Types.Constructor open VSharp.Utils @@ -517,38 +516,25 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - let pcConstants = HashSet(state.pc.Constants) - let condConstants = condition |> Seq.singleton |> discoverConstants - let noNewConstants = pcConstants.IsSupersetOf condConstants - let evaluatedCondition = state.model.Value.Eval condition - // Current model satisfies new condition, so we can keep it for 'then' branch - if (noNewConstants && (isTrue evaluatedCondition)) then - let elseState = copy conditionState independentElsePc - conditionState.pc <- thenPc - match SolverInteraction.checkSat elseState with + conditionState.pc <- independentThenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> - thenBranch conditionState (List.singleton >> k) + __insufficientInformation__ "Unable to witness branch" | SolverInteraction.SmtSat elseModel -> - elseState.pc <- elsePc - elseState.model <- Some elseModel.mdl - execution conditionState elseState condition k - // Current model satisfies !condition, so we can keep it for 'else' branch - elif (noNewConstants && (isFalse evaluatedCondition)) then - let thenState = copy conditionState independentThenPc - match SolverInteraction.checkSat thenState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - let elseState = copy conditionState elsePc - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - execution conditionState elseState condition k - else - conditionState.pc <- independentThenPc + | SolverInteraction.SmtUnsat _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- independentElsePc match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> conditionState.model <- Some thenModel.mdl conditionState.pc <- thenPc diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index defa96241..ca7c85831 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -205,11 +205,6 @@ module public PC = Some(PathCondition(constants, constraints, false)) | _ -> None Seq.choose getSubsetByRepresentative constants.Values - - /// - /// Returns all constants contained in path condition - /// - member this.Constants = seq constants.Keys let public add newConstraint (pc : PathCondition) = let copy = pc.Copy() From cbadf8c87acff5af891d03fc7bb18d0e2557fe58 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 13:03:43 +0300 Subject: [PATCH 67/88] Very dirty attempt to eval model before --- VSharp.SILI.Core/Memory.fs | 78 +++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 5fe67dcaf..f69dc19bc 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -5,6 +5,7 @@ open System.Collections.Generic open System.Text open FSharpx.Collections open VSharp +open VSharp.Core open VSharp.Core.Types open VSharp.Core.Types.Constructor open VSharp.Utils @@ -516,36 +517,69 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - conditionState.pc <- independentThenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with + let pcConstants = state.pc.ToSeq() |> discoverConstants + let condConstants = condition |> Seq.singleton |> discoverConstants + let noNewConstants = pcConstants.IsSupersetOf condConstants + let kek = state.model.Value.Eval condition + if (noNewConstants && (isTrue kek)) then + Logger.trace $"Current model satisfies {condition}" + let elseState = copy conditionState independentElsePc + conditionState.pc <- thenPc + conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model + match SolverInteraction.checkSat elseState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" + thenBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- elsePc - conditionState.model <- Some elseModel.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> + elseState.pc <- elsePc + elseState.model <- Some elseModel.mdl + execution conditionState elseState condition k + elif (noNewConstants && (isFalse kek)) then + Logger.trace $"Current model satisfies {negatedCondition}" + let thenState = copy conditionState independentThenPc conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with + conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model + match SolverInteraction.checkSat thenState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> - conditionState.model <- Some thenModel.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + let elseState = copy conditionState elsePc conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState elsePc - elseState.model <- Some elseModel.mdl - thenState.pc <- thenPc - execution thenState elseState condition k) + execution conditionState elseState condition k + else + Logger.trace "Current model doesn't satisfy condition or its negation" + conditionState.pc <- independentThenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.model <- Some thenModel.mdl + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState elsePc + elseState.model <- Some elseModel.mdl + thenState.pc <- thenPc + execution thenState elseState condition k) let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k From 3291c7415be87b9e5231707f808d847be5e0bddc Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 10 May 2022 11:20:24 +0300 Subject: [PATCH 68/88] [style] Prettify code a bit --- VSharp.SILI.Core/Memory.fs | 17 +++++++---------- VSharp.SILI.Core/PathCondition.fs | 5 +++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index f69dc19bc..a5daaf477 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -517,15 +517,14 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - let pcConstants = state.pc.ToSeq() |> discoverConstants + let pcConstants = HashSet(state.pc.Constants) let condConstants = condition |> Seq.singleton |> discoverConstants let noNewConstants = pcConstants.IsSupersetOf condConstants - let kek = state.model.Value.Eval condition - if (noNewConstants && (isTrue kek)) then - Logger.trace $"Current model satisfies {condition}" + let evaluatedCondition = state.model.Value.Eval condition + // Current model satisfies new condition, so we can keep it for 'then' branch + if (noNewConstants && (isTrue evaluatedCondition)) then let elseState = copy conditionState independentElsePc conditionState.pc <- thenPc - conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model match SolverInteraction.checkSat elseState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> @@ -534,14 +533,13 @@ module internal Memory = elseState.pc <- elsePc elseState.model <- Some elseModel.mdl execution conditionState elseState condition k - elif (noNewConstants && (isFalse kek)) then - Logger.trace $"Current model satisfies {negatedCondition}" + // Current model satisfies !condition, so we can keep it for 'else' branch + elif (noNewConstants && (isFalse evaluatedCondition)) then let thenState = copy conditionState independentThenPc - conditionState.pc <- elsePc - conditionState.model <- Option.bind (fun mdl -> Some {mdl with state = {mdl.state with model = mdl.state.model}}) conditionState.model match SolverInteraction.checkSat thenState with | SolverInteraction.SmtUnsat _ | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc elseBranch conditionState (List.singleton >> k) | SolverInteraction.SmtSat thenModel -> let elseState = copy conditionState elsePc @@ -549,7 +547,6 @@ module internal Memory = conditionState.model <- Some thenModel.mdl execution conditionState elseState condition k else - Logger.trace "Current model doesn't satisfy condition or its negation" conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnknown _ -> diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index ca7c85831..defa96241 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -205,6 +205,11 @@ module public PC = Some(PathCondition(constants, constraints, false)) | _ -> None Seq.choose getSubsetByRepresentative constants.Values + + /// + /// Returns all constants contained in path condition + /// + member this.Constants = seq constants.Keys let public add newConstraint (pc : PathCondition) = let copy = pc.Copy() From 78cd8c16460bcb9ab75c9cc2b5e24948eddf049b Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 13:57:30 +0300 Subject: [PATCH 69/88] [fix] Remove noNewConsts --- VSharp.SILI.Core/Memory.fs | 9 +++++---- VSharp.Solver/Z3.fs | 4 ++-- VSharp.Test/IntegrationTests.cs | 8 ++++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index a5daaf477..2cf860b44 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -517,12 +517,12 @@ module internal Memory = conditionState.pc <- thenPc thenBranch conditionState (List.singleton >> k) else - let pcConstants = HashSet(state.pc.Constants) +(* let pcConstants = HashSet(state.pc.Constants) let condConstants = condition |> Seq.singleton |> discoverConstants - let noNewConstants = pcConstants.IsSupersetOf condConstants + let noNewConstants = pcConstants.IsSupersetOf condConstants*) let evaluatedCondition = state.model.Value.Eval condition // Current model satisfies new condition, so we can keep it for 'then' branch - if (noNewConstants && (isTrue evaluatedCondition)) then + if isTrue evaluatedCondition then let elseState = copy conditionState independentElsePc conditionState.pc <- thenPc match SolverInteraction.checkSat elseState with @@ -534,7 +534,7 @@ module internal Memory = elseState.model <- Some elseModel.mdl execution conditionState elseState condition k // Current model satisfies !condition, so we can keep it for 'else' branch - elif (noNewConstants && (isFalse evaluatedCondition)) then + elif isFalse evaluatedCondition then let thenState = copy conditionState independentThenPc match SolverInteraction.checkSat thenState with | SolverInteraction.SmtUnsat _ @@ -547,6 +547,7 @@ module internal Memory = conditionState.model <- Some thenModel.mdl execution conditionState elseState condition k else + Logger.trace $"Evaluated condition != False and != True: {evaluatedCondition}" conditionState.pc <- independentThenPc match SolverInteraction.checkSat conditionState with | SolverInteraction.SmtUnknown _ -> diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 941d61c6f..61107400f 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -802,7 +802,7 @@ module internal Z3 = yield query.expr } |> Array.ofSeq // let pathAtoms = addSoftConstraints q.lvl - let result = optCtx.Check assumptions + let result = Stopwatch.runMeasuringTime "Z3_check" (fun _ -> optCtx.Check assumptions) match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model @@ -873,7 +873,7 @@ module internal Z3 = let names = boolConsts |> Seq.map (fun c -> c.ToString()) let amp = " & " printLog Trace $"SOLVER: check: {join amp names}" - let result = optCtx.Check boolConsts + let result = Stopwatch.runMeasuringTime "Z3_check_assumptions" (fun _ -> optCtx.Check boolConsts) match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 924b32c2f..bb96385c7 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -108,8 +108,12 @@ private TestResult Explore(TestExecutionContext context) SILI explorer = new SILI(_options); UnitTests unitTests = new UnitTests(Directory.GetCurrentDirectory()); - explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); - + Stopwatch.runMeasuringTime("total", FuncConvert.FromAction(() => + { + explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); + } + )); + if (unitTests.UnitTestsCount == 0 && unitTests.ErrorsCount == 0 && explorer.Statistics.IncompleteStates.Count == 0) { throw new Exception("No states were obtained! Most probably this is bug."); From 35e47569dd108b7e45103e86f79c46d1745a7c79 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 14:51:27 +0300 Subject: [PATCH 70/88] [fix] Save number of generated unit tests to csv --- VSharp.Test/IntegrationTests.cs | 2 +- VSharp.Utils/Stopwatch.fs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index bb96385c7..2885d7aab 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -138,7 +138,7 @@ private TestResult Explore(TestExecutionContext context) context.CurrentResult.SetResult(ResultState.Success); } - Stopwatch.saveMeasurements(methodInfo.Name); + Stopwatch.saveMeasurements(methodInfo.Name, unitTests.UnitTestsCount); Stopwatch.clear(); } catch (Exception e) diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index f83acc22b..8fc75667c 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -23,6 +23,7 @@ module Stopwatch = timesCalled: int totalTicks: int64 totalMs: int64 + testsGenerated: uint } let private csvPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VSharpBenchmark") @@ -80,7 +81,8 @@ module Stopwatch = /// Saves all current measurement results to .csv file. If the file already exists, appends lines /// /// Additional tag given to the saved measurements - let public saveMeasurements caseName = + /// Number of tests generated during the run + let public saveMeasurements caseName testsGenerated = stopAll() let commitHash = getGitCommitHash() @@ -98,6 +100,7 @@ module Stopwatch = timesCalled = m.timesCalled totalTicks = m.stopwatch.ElapsedTicks totalMs = m.stopwatch.ElapsedMilliseconds + testsGenerated = testsGenerated } ) From 40edf31bd5f5d5ce2edadc18405e3b50abfcd60f Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 16:37:17 +0300 Subject: [PATCH 71/88] [fix] Make regex test harder --- VSharp.Test/Tests/RegExTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VSharp.Test/Tests/RegExTest.cs b/VSharp.Test/Tests/RegExTest.cs index d280823d3..2bad28a77 100644 --- a/VSharp.Test/Tests/RegExTest.cs +++ b/VSharp.Test/Tests/RegExTest.cs @@ -51,9 +51,9 @@ public static bool Match(string re, string text) public class RegExTest { [TestSvm(100)] - public static string OwnImplementationTest(char c1, char c2, char c3, char c4, char c5, char c6) + public static string OwnImplementationTest(char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8) { - string pattern = new string(new char[] {c1, c2, c3}); + string pattern = new string(new char[] {c1, c2, c3, c4, c5, c6, c7, c8}); string result = ""; if (RegExImplementation.Match(pattern, "hello")) { From defb901c2f988bbd97ba9dc5e2661c62ec3e8c04 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 17:20:59 +0300 Subject: [PATCH 72/88] [fix] Set expected coverage for new tests --- VSharp.Test/Tests/SolverBenchmark.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/VSharp.Test/Tests/SolverBenchmark.cs b/VSharp.Test/Tests/SolverBenchmark.cs index 75a615377..46e209f17 100644 --- a/VSharp.Test/Tests/SolverBenchmark.cs +++ b/VSharp.Test/Tests/SolverBenchmark.cs @@ -22,7 +22,7 @@ private static int CollatzStep(int a) return 3 * a + 1; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb1(int i) { if (i <= 0) @@ -47,7 +47,7 @@ public static int CollatzLogicBomOmb1(int i) return 2; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb2(int i) { if (i <= 0 || i >= 100) @@ -84,7 +84,7 @@ private static int CustomCollatzStep(int a, int b) } // Fails with j == 11 and loopCount == 14 - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb3(int i) { var j = 12; @@ -104,7 +104,7 @@ public static int CollatzLogicBomOmb3(int i) return 2; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb4(int i, int j) { var loopCount = 0; @@ -130,7 +130,7 @@ public static int CollatzLogicBomOmb4(int i, int j) return 2; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb5(int i, int j, int k) { var loopCount = 0; @@ -162,7 +162,7 @@ public static int CollatzLogicBomOmb5(int i, int j, int k) return 2; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb6(int i, int j) { var loopCount = 0; @@ -183,7 +183,7 @@ public static int CollatzLogicBomOmb6(int i, int j) return 2; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb7(int i) { for (var j = 1; j < 100; ++j) @@ -199,7 +199,7 @@ public static int CollatzLogicBomOmb7(int i) return 0; } - [TestSvm] + [TestSvm(100)] public static int CollatzLogicBomOmb8(int i, int j) { for (var k = 1; k < 17; ++k) From fd05f66cb988f1030ac1f60a10f8e7ca93be529d Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 17 May 2022 22:20:14 +0300 Subject: [PATCH 73/88] [fix] Add PC class without independence --- VSharp.SILI.Core/API.fs | 8 +- VSharp.SILI.Core/API.fsi | 10 +- VSharp.SILI.Core/Memory.fs | 86 ++++++++++++++++- VSharp.SILI.Core/Merging.fs | 2 +- VSharp.SILI.Core/PathCondition.fs | 155 ++++++++++++++++++++---------- VSharp.SILI.Core/State.fs | 2 +- VSharp.SILI/Statistics.fs | 2 +- 7 files changed, 201 insertions(+), 64 deletions(-) diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index a2e4b143f..b82c063fa 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -233,8 +233,8 @@ module API = let Contradicts state condition = let copy = PC.add condition state.pc copy.IsFalse - let PathConditionToSeq (pc : PC.PathCondition) = pc.ToSeq() - let EmptyPathCondition() = PC.PathCondition() + let PathConditionToSeq (pc : PC.IPathCondition) = pc.ToSeq() + let EmptyPathCondition() = PC.PathCondition() :> PC.IPathCondition module Types = let Numeric t = Types.Numeric t @@ -584,7 +584,7 @@ module API = | _ -> internalfailf "constructing string from char array: expected string reference, but got %O" dstRef let ComposeStates state state' = Memory.composeStates state state' - let WLP state (pc' : PC.PathCondition) = PC.map (Memory.fillHoles state) pc' |> PC.unionWith state.pc + let WLP state (pc' : PC.IPathCondition) = PC.map (Memory.fillHoles state) pc' |> PC.unionWith state.pc let Merge2States (s1 : state) (s2 : state) = Memory.merge2States s1 s2 let Merge2Results (r1, s1 : state) (r2, s2 : state) = Memory.merge2Results (r1, s1) (r2, s2) @@ -612,4 +612,4 @@ module API = module Print = let Dump state = Memory.dump state - let PrintPC (pc : PC.PathCondition) = pc.ToString() + let PrintPC (pc : PC.IPathCondition) = pc.ToString() diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index ca62a04eb..eb4131e52 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -18,7 +18,7 @@ module API = val StatedConditionalExecutionAppendResults : (state -> (state -> (term * state -> 'a) -> 'b) -> (state -> (state list -> 'a) -> 'a) -> (state -> (state list -> 'a) -> 'a) -> (state list -> 'a) -> 'b) val GuardedApplyExpression : term -> (term -> term) -> term - val GuardedApplyExpressionWithPC : PC.PathCondition -> term -> (term -> term) -> term + val GuardedApplyExpressionWithPC : PC.IPathCondition -> term -> (term -> term) -> term val GuardedStatedApplyStatementK : state -> term -> (state -> term -> (term * state -> 'a) -> 'a) -> ((term * state) list -> 'a) -> 'a val GuardedStatedApplyk : (state -> term -> ('item -> 'a) -> 'a) -> state -> term -> ('item list -> 'item list) -> ('item list -> 'a) -> 'a @@ -105,8 +105,8 @@ module API = val AddConstraint : state -> term -> unit val IsFalsePathCondition : state -> bool val Contradicts : state -> term -> bool - val PathConditionToSeq : PC.PathCondition -> term seq - val EmptyPathCondition : unit -> PC.PathCondition + val PathConditionToSeq : PC.IPathCondition -> term seq + val EmptyPathCondition : unit -> PC.IPathCondition module Types = val Numeric : System.Type -> symbolicType @@ -286,7 +286,7 @@ module API = // TODO: get rid of all unnecessary stuff below! val ComposeStates : state -> state -> state list - val WLP : state -> PC.PathCondition -> PC.PathCondition + val WLP : state -> PC.IPathCondition -> PC.IPathCondition val Merge2States : state -> state -> state list val Merge2Results : term * state -> term * state -> (term * state) list @@ -297,7 +297,7 @@ module API = module Print = val Dump : state -> string - val PrintPC : PC.PathCondition -> string + val PrintPC : PC.IPathCondition -> string // module Marshalling = // val Unmarshal : state -> obj -> term * state diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 2cf860b44..c4af35ca4 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -493,8 +493,8 @@ module internal Memory = let guardedStatedMap mapper state term = commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id - let commonStatedConditionalExecutionk (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let keepDependentWith (pc : PC.PathCondition) cond = + let executionWithConstraintIndependence (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let keepDependentWith (pc : PC.IPathCondition) cond = pc.Fragments |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) |> Option.defaultValue pc @@ -578,6 +578,88 @@ module internal Memory = elseState.model <- Some elseModel.mdl thenState.pc <- thenPc execution thenState elseState condition k) + + let executionWithoutConstraintIndependence (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let execution thenState elseState condition k = + assert (condition <> True && condition <> False) + thenBranch thenState (fun thenResult -> + elseBranch elseState (fun elseResult -> + merge2Results thenResult elseResult |> k)) + conditionInvocation state (fun (condition, conditionState) -> + let negatedCondition = !!condition + let thenPc = PC.add condition state.pc + let elsePc = PC.add negatedCondition state.pc + if thenPc.IsFalse then + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + elif elsePc.IsFalse then + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + else +(* let pcConstants = HashSet(state.pc.Constants) + let condConstants = condition |> Seq.singleton |> discoverConstants + let noNewConstants = pcConstants.IsSupersetOf condConstants*) + let evaluatedCondition = state.model.Value.Eval condition + // Current model satisfies new condition, so we can keep it for 'then' branch + if isTrue evaluatedCondition then + let elseState = copy conditionState elsePc + conditionState.pc <- thenPc + match SolverInteraction.checkSat elseState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + elseState.pc <- elsePc + elseState.model <- Some elseModel.mdl + execution conditionState elseState condition k + // Current model satisfies !condition, so we can keep it for 'else' branch + elif isFalse evaluatedCondition then + let thenState = copy conditionState thenPc + match SolverInteraction.checkSat thenState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + let elseState = copy conditionState elsePc + conditionState.pc <- thenPc + conditionState.model <- Some thenModel.mdl + execution conditionState elseState condition k + else + Logger.trace $"Evaluated condition != False and != True: {evaluatedCondition}" + conditionState.pc <- thenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- elsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.model <- Some thenModel.mdl + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState elsePc + elseState.model <- Some elseModel.mdl + thenState.pc <- thenPc + execution thenState elseState condition k) + + let commonStatedConditionalExecutionk = + executionWithoutConstraintIndependence let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k diff --git a/VSharp.SILI.Core/Merging.fs b/VSharp.SILI.Core/Merging.fs index 520dc7a06..4108e88dd 100644 --- a/VSharp.SILI.Core/Merging.fs +++ b/VSharp.SILI.Core/Merging.fs @@ -102,7 +102,7 @@ module internal Merging = let guardedApplyk f term k = commonGuardedApplyk f term merge k let guardedApply f term = guardedApplyk (Cps.ret f) term id - let commonGuardedMapkWithPC (pc : PC.PathCondition) mapper gvs merge k = + let commonGuardedMapkWithPC (pc : PC.IPathCondition) mapper gvs merge k = let foldFunc gvs (g, v) k = let pc' = PC.add g pc if pc'.IsFalse then k gvs diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index defa96241..308b9b8a1 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -5,6 +5,63 @@ open System.Collections.Generic module public PC = + type IPathCondition = + abstract Add : term -> unit + abstract Copy : unit -> IPathCondition + abstract ToSeq : unit -> term seq + abstract UnionWith : IPathCondition -> IPathCondition + abstract Map : (term -> term) -> IPathCondition + abstract IsEmpty : bool + abstract IsFalse : bool + abstract Fragments : IPathCondition seq + + type public PathCondition private (constraints : HashSet, isFalse : bool) = + + let mutable isFalse = isFalse + + let becomeTrivialFalse() = + constraints.Clear() + isFalse <- true + + new() = + PathCondition(HashSet(), false) + + override this.ToString() = + Seq.map toString constraints |> Seq.sort |> join " /\ " + + interface IPathCondition with + + member this.Add newConstraint = + match newConstraint with + | True -> () + | False -> becomeTrivialFalse() + | _ when isFalse -> () + | _ when constraints.Contains(newConstraint) -> () + // what if constraint is not equal to newConstraint structurally, but is equal logically? + | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() + | _ -> constraints.Add(newConstraint) |> ignore + + member this.Copy() = + PathCondition(HashSet(constraints), isFalse) + + member this.ToSeq() = seq constraints + + member this.UnionWith (anotherPc : IPathCondition) = + let union = (this :> IPathCondition).Copy() + anotherPc.ToSeq() |> Seq.iter union.Add + union + + member this.Map mapper = + let mapped = PathCondition() :> IPathCondition + Seq.iter (mapper >> mapped.Add) constraints + mapped + + member this.IsEmpty = constraints.Count = 0 + + member this.IsFalse = isFalse + + member this.Fragments = Seq.singleton this + type private node = | Tail of term * term pset | Node of term @@ -29,7 +86,7 @@ module public PC = isFalse -- flag used to determine if the PC is false trivially (i. e. c an !c were added to it). Invariant: PC doesn't contain True or False as elements. *) - type public PathCondition private(constants : Dictionary, constraints : HashSet, isFalse : bool) = + type public IndependentPathCondition private (constants : Dictionary, constraints : HashSet, isFalse : bool) = let mutable isFalse = isFalse @@ -154,70 +211,68 @@ module public PC = | _ -> () new() = - PathCondition(Dictionary(), HashSet(), false) + IndependentPathCondition(Dictionary(), HashSet(), false) override this.ToString() = Seq.map toString constraints |> Seq.sort |> join " /\ " + + interface IPathCondition with - member this.Copy() = - PathCondition(Dictionary(constants), HashSet(constraints), isFalse) + member this.Copy() = + IndependentPathCondition(Dictionary(constants), HashSet(constraints), isFalse) - member this.IsFalse = isFalse + member this.IsFalse = isFalse - member this.IsEmpty = constraints.Count = 0 + member this.IsEmpty = constraints.Count = 0 - member this.ToSeq() = seq constraints + member this.ToSeq() = seq constraints - member this.Add newConstraint = - match newConstraint with - | True -> () - | False -> becomeTrivialFalse() - | _ when isFalse -> () - | _ when constraints.Contains(newConstraint) -> () - // what if constraint is not equal to newConstraint structurally, but is equal logically? - | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() - | _ -> addNewConstraintWithMerge newConstraint + member this.Add newConstraint = + match newConstraint with + | True -> () + | False -> becomeTrivialFalse() + | _ when isFalse -> () + | _ when constraints.Contains(newConstraint) -> () + // what if constraint is not equal to newConstraint structurally, but is equal logically? + | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() + | _ -> addNewConstraintWithMerge newConstraint - member this.Map mapper = - let mapped = PathCondition() - Seq.iter (mapper >> mapped.Add) constraints - mapped + member this.Map mapper = + let mapped = IndependentPathCondition() :> IPathCondition + Seq.iter (mapper >> mapped.Add) constraints + mapped - member this.UnionWith (anotherPc : PathCondition) = - let union = this.Copy() - anotherPc.ToSeq() |> Seq.iter union.Add - union + member this.UnionWith (anotherPc : IPathCondition) = + let union = (this :> IPathCondition).Copy() + anotherPc.ToSeq() |> Seq.iter union.Add + union - /// - /// Returns the sequence of path conditions such that constants contained in - /// one path condition are independent with constants contained in another one - /// - member this.Fragments = - if isFalse then - Seq.singleton this - else - let getSubsetByRepresentative = - function - | Tail(representative, constraints) -> - let constants = Dictionary() - addSubset constants (subset representative) constraints - let constraints = HashSet(PersistentSet.toSeq constraints) - Some(PathCondition(constants, constraints, false)) - | _ -> None - Seq.choose getSubsetByRepresentative constants.Values - - /// - /// Returns all constants contained in path condition - /// - member this.Constants = seq constants.Keys + /// + /// Returns the sequence of path conditions such that constants contained in + /// one path condition are independent with constants contained in another one + /// + member this.Fragments = + if isFalse then + Seq.singleton this + else + let getSubsetByRepresentative = + function + | Tail(representative, constraints) -> + let constants = Dictionary() + addSubset constants (subset representative) constraints + let constraints = HashSet(PersistentSet.toSeq constraints) + Some(IndependentPathCondition(constants, constraints, false)) + | _ -> None + Seq.choose getSubsetByRepresentative constants.Values + |> Seq.cast - let public add newConstraint (pc : PathCondition) = + let public add newConstraint (pc : IPathCondition) = let copy = pc.Copy() copy.Add newConstraint copy - let public toSeq (pc : PathCondition) = pc.ToSeq() + let public toSeq (pc : IPathCondition) = pc.ToSeq() - let public map mapper (pc : PathCondition) = pc.Map mapper + let public map mapper (pc : IPathCondition) = pc.Map mapper - let public unionWith anotherPc (pc : PathCondition) = pc.UnionWith anotherPc + let public unionWith anotherPc (pc : IPathCondition) = pc.UnionWith anotherPc diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index a3444afbc..4b04c405d 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -117,7 +117,7 @@ and [] state = { id : string - mutable pc : PC.PathCondition + mutable pc : PC.IPathCondition mutable evaluationStack : evaluationStack mutable stack : callStack // Arguments and local variables mutable stackBuffers : pdict // Buffers allocated via stackAlloc diff --git a/VSharp.SILI/Statistics.fs b/VSharp.SILI/Statistics.fs index f14319f15..d04b524dc 100644 --- a/VSharp.SILI/Statistics.fs +++ b/VSharp.SILI/Statistics.fs @@ -12,7 +12,7 @@ open VSharp.Utils open CilStateOperations open ipOperations -type pob = {loc : codeLocation; lvl : uint; pc : PC.PathCondition} +type pob = {loc : codeLocation; lvl : uint; pc : PC.IPathCondition} with override x.ToString() = sprintf "loc = %O; lvl = %d; pc = %s" x.loc x.lvl (Print.PrintPC x.pc) From 9a592c2bf0f73c6b6973f4816841513562deb629 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Sun, 22 May 2022 22:28:37 +0300 Subject: [PATCH 74/88] [feat] Add ability to enable/disable opts with flags --- VSharp.API/Parameters.cs | 18 +++ VSharp.API/VSharp.cs | 69 +++++---- VSharp.Runner/RunnerProgram.cs | 34 ++-- VSharp.SILI.Core/API.fs | 11 +- VSharp.SILI.Core/API.fsi | 12 +- VSharp.SILI.Core/Branching.fs | 189 +++++++++++++++++++++++ VSharp.SILI.Core/FeatureFlags.fs | 18 +++ VSharp.SILI.Core/Memory.fs | 175 +-------------------- VSharp.SILI.Core/Merging.fs | 2 +- VSharp.SILI.Core/PathCondition.fs | 49 ++++-- VSharp.SILI.Core/SolverInteraction.fs | 8 +- VSharp.SILI.Core/State.fs | 4 +- VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 2 + VSharp.SILI/Statistics.fs | 2 +- VSharp.Test/IntegrationTests.cs | 6 +- VSharp.Utils/Logger.fs | 5 +- VSharp.Utils/Stopwatch.fs | 15 +- VSharp.Utils/UnitTests.fs | 4 +- 18 files changed, 370 insertions(+), 253 deletions(-) create mode 100644 VSharp.API/Parameters.cs create mode 100644 VSharp.SILI.Core/Branching.fs create mode 100644 VSharp.SILI.Core/FeatureFlags.fs diff --git a/VSharp.API/Parameters.cs b/VSharp.API/Parameters.cs new file mode 100644 index 000000000..f7708fb1b --- /dev/null +++ b/VSharp.API/Parameters.cs @@ -0,0 +1,18 @@ +using VSharp.Core; + +namespace VSharp; + +public readonly record struct Parameters( + bool IsConstraintIndependenceEnabled, + bool IsConditionEvalEnabled, + bool IsIncrementalityEnabled, + string RunId = "" +) +{ + public featureFlags GetFeatureFlags() => + new featureFlags( + isConstraintIndependenceEnabled: IsConstraintIndependenceEnabled, + isConditionEvalEnabled: IsConditionEvalEnabled, + isIncrementalityEnabled: IsIncrementalityEnabled + ); +} \ No newline at end of file diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index 531e72c8d..d1bdafd6c 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -3,6 +3,8 @@ using System.IO; using System.Linq; using System.Reflection; +using Microsoft.FSharp.Core; +using VSharp.Core; using VSharp.Interpreter.IL; using VSharp.Solver; @@ -71,7 +73,7 @@ public void GenerateReport(TextWriter writer) public static class TestGenerator { - private static Statistics StartExploration(List methods, string resultsFolder, string[] mainArguments = null) + private static Statistics StartExploration(List methods, string resultsFolder, Parameters parameters, string[] mainArguments = null) { var recThreshold = 0u; var options = @@ -80,18 +82,31 @@ private static Statistics StartExploration(List methods, string resu SILI explorer = new SILI(options); UnitTests unitTests = new UnitTests(resultsFolder); Core.API.ConfigureSolver(SolverPool.mkSolver()); + Core.API.SetFeatureFlags(parameters.GetFeatureFlags()); + + var totalTestsGenerated = 0u; + foreach (var method in methods) { - if (method == method.Module.Assembly.EntryPoint) - { - explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, - e => throw e); - } - else - { - explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, - e => throw e); - } + Stopwatch.runMeasuringTime("total", FuncConvert.FromAction(() => + { + if (method == method.Module.Assembly.EntryPoint) + { + explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, + e => throw e); + } + else + { + explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, + e => throw e); + } + } + )); + + Stopwatch.saveMeasurements(parameters.RunId, method.Name, unitTests.UnitTestsCount - totalTestsGenerated); + Stopwatch.clear(); + + totalTestsGenerated = unitTests.UnitTestsCount; } var statistics = new Statistics(explorer.Statistics.CurrentExplorationTime, unitTests.TestDirectory, @@ -112,10 +127,10 @@ private static bool Reproduce(DirectoryInfo testDir) /// Type to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. /// Summary of tests generation process. - public static Statistics Cover(MethodBase method, string outputDirectory = "") + public static Statistics Cover(MethodBase method, Parameters parameters, string outputDirectory = "") { List methods = new List {method}; - return StartExploration(methods, outputDirectory); + return StartExploration(methods, outputDirectory, parameters); } /// @@ -125,7 +140,7 @@ public static Statistics Cover(MethodBase method, string outputDirectory = "") /// Directory to place generated *.vst tests. If null or empty, process working directory is used. /// Summary of tests generation process. /// Thrown if specified class does not contain public methods. - public static Statistics Cover(Type type, string outputDirectory = "") + public static Statistics Cover(Type type, Parameters parameters, string outputDirectory = "") { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; @@ -140,7 +155,7 @@ public static Statistics Cover(Type type, string outputDirectory = "") throw new ArgumentException("I've not found any public method of class " + type.FullName); } - return StartExploration(methods, outputDirectory); + return StartExploration(methods, outputDirectory, parameters); } /// @@ -151,7 +166,7 @@ public static Statistics Cover(Type type, string outputDirectory = "") /// Summary of tests generation process. /// Thrown if no public methods found in assembly. /// - public static Statistics Cover(Assembly assembly, string outputDirectory = "") + public static Statistics Cover(Assembly assembly, Parameters parameters, string outputDirectory = "") { List methods; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | @@ -173,7 +188,7 @@ public static Statistics Cover(Assembly assembly, string outputDirectory = "") throw new ArgumentException("I've not found any public method in assembly"); } - return StartExploration(methods, outputDirectory); + return StartExploration(methods, outputDirectory, parameters); } /// @@ -185,7 +200,7 @@ public static Statistics Cover(Assembly assembly, string outputDirectory = "") /// Summary of tests generation process. /// Thrown if assembly does not contain entry point. /// - public static Statistics Cover(Assembly assembly, string[] args, string outputDirectory = "") + public static Statistics Cover(Assembly assembly, Parameters parameters, string[] args, string outputDirectory = "") { List methods; var entryPoint = assembly.EntryPoint; @@ -195,7 +210,7 @@ public static Statistics Cover(Assembly assembly, string[] args, string outputDi } methods = new List { entryPoint }; - return StartExploration(methods, outputDirectory, args); + return StartExploration(methods, outputDirectory, parameters, args); } /// @@ -204,9 +219,9 @@ public static Statistics Cover(Assembly assembly, string[] args, string outputDi /// Type to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. /// True if all generated tests have passed. - public static bool CoverAndRun(MethodBase method, string outputDirectory = "") + public static bool CoverAndRun(MethodBase method, Parameters parameters, string outputDirectory = "") { - var stats = Cover(method, outputDirectory); + var stats = Cover(method, parameters, outputDirectory); return Reproduce(stats.OutputDir); } @@ -217,9 +232,9 @@ public static bool CoverAndRun(MethodBase method, string outputDirectory = "") /// Directory to place generated *.vst tests. If null or empty, process working directory is used. /// True if all generated tests have passed. /// Thrown if specified class does not contain public methods. - public static bool CoverAndRun(Type type, string outputDirectory = "") + public static bool CoverAndRun(Type type, Parameters parameters, string outputDirectory = "") { - var stats = Cover(type, outputDirectory); + var stats = Cover(type, parameters, outputDirectory); return Reproduce(stats.OutputDir); } @@ -231,9 +246,9 @@ public static bool CoverAndRun(Type type, string outputDirectory = "") /// True if all generated tests have passed. /// Thrown if no public methods found in assembly. /// - public static bool CoverAndRun(Assembly assembly, string outputDirectory = "") + public static bool CoverAndRun(Assembly assembly, Parameters parameters, string outputDirectory = "") { - var stats = Cover(assembly, outputDirectory); + var stats = Cover(assembly, parameters, outputDirectory); return Reproduce(stats.OutputDir); } @@ -245,9 +260,9 @@ public static bool CoverAndRun(Assembly assembly, string outputDirectory = "") /// Directory to place generated *.vst tests. If null or empty, process working directory is used. /// True if all generated tests have passed. /// Thrown if assembly does not contain entry point. - public static bool CoverAndRun(Assembly assembly, string[] args, string outputDirectory = "") + public static bool CoverAndRun(Assembly assembly, Parameters parameters, string[] args, string outputDirectory = "") { - var stats = Cover(assembly, args, outputDirectory); + var stats = Cover(assembly, parameters, args, outputDirectory); return Reproduce(stats.OutputDir); } diff --git a/VSharp.Runner/RunnerProgram.cs b/VSharp.Runner/RunnerProgram.cs index 2b1821593..575924dec 100644 --- a/VSharp.Runner/RunnerProgram.cs +++ b/VSharp.Runner/RunnerProgram.cs @@ -122,6 +122,11 @@ public static int Main(string[] args) var unknownArgsOption = new Option("--unknown-args", description: "Force engine to generate various input console arguments"); + var constraintIndependenceOption = new Option("--cind"); + var evalConditionOption = new Option("--eval-cond"); + var incrementalityOption = new Option("--incrementality"); + var runIdOption = new Option("--runId"); + var rootCommand = new RootCommand(); var entryPointCommand = @@ -131,18 +136,25 @@ public static int Main(string[] args) entryPointCommand.AddArgument(concreteArguments); entryPointCommand.AddGlobalOption(outputOption); entryPointCommand.AddOption(unknownArgsOption); + var allPublicMethodsCommand = new Command("--all-public-methods", "Generate unit tests for all public methods of all public classes of assembly"); rootCommand.AddCommand(allPublicMethodsCommand); allPublicMethodsCommand.AddArgument(assemblyPathArgument); allPublicMethodsCommand.AddGlobalOption(outputOption); + var publicMethodsOfClassCommand = - new Command("--public-methods-of-class", "Generate unit tests for all public methods of specified class"); + new Command("public-methods-of-class", "Generate unit tests for all public methods of specified class"); rootCommand.AddCommand(publicMethodsOfClassCommand); var classArgument = new Argument("class-name"); publicMethodsOfClassCommand.AddArgument(classArgument); publicMethodsOfClassCommand.AddArgument(assemblyPathArgument); publicMethodsOfClassCommand.AddGlobalOption(outputOption); + publicMethodsOfClassCommand.AddOption(runIdOption); + publicMethodsOfClassCommand.AddOption(constraintIndependenceOption); + publicMethodsOfClassCommand.AddOption(evalConditionOption); + publicMethodsOfClassCommand.AddOption(incrementalityOption); + var specificMethodCommand = new Command("--method", "Try to resolve and generate unit test coverage for the specified method"); rootCommand.AddCommand(specificMethodCommand); @@ -153,41 +165,45 @@ public static int Main(string[] args) rootCommand.Description = "Symbolic execution engine for .NET"; - entryPointCommand.Handler = CommandHandler.Create((assemblyPath, args, output, unknownArgs) => + entryPointCommand.Handler = CommandHandler.Create((assemblyPath, args, output, unknownArgs, ci, eval, inc, id) => { + var parameters = new Parameters(ci, eval, inc, id); var assembly = ResolveAssembly(assemblyPath); if (unknownArgs) args = null; if (assembly != null) - PostProcess(TestGenerator.Cover(assembly, args, output.FullName)); + PostProcess(TestGenerator.Cover(assembly, parameters, args, output.FullName)); }); - allPublicMethodsCommand.Handler = CommandHandler.Create((assemblyPath, output) => + allPublicMethodsCommand.Handler = CommandHandler.Create((assemblyPath, output, ci, eval, inc, id) => { + var parameters = new Parameters(ci, eval, inc, id); var assembly = ResolveAssembly(assemblyPath); if (assembly != null) - PostProcess(TestGenerator.Cover(assembly, output.FullName)); + PostProcess(TestGenerator.Cover(assembly, parameters, output.FullName)); }); - publicMethodsOfClassCommand.Handler = CommandHandler.Create((className, assemblyPath, output) => + publicMethodsOfClassCommand.Handler = CommandHandler.Create((className, assemblyPath, output, runId, ci, eval, inc) => { + var parameters = new Parameters(ci, eval, inc, runId); var assembly = ResolveAssembly(assemblyPath); if (assembly != null) { var type = ResolveType(assembly, className); if (type != null) { - PostProcess(TestGenerator.Cover(type, output.FullName)); + PostProcess(TestGenerator.Cover(type, parameters, output.FullName)); } } }); - specificMethodCommand.Handler = CommandHandler.Create((methodName, assemblyPath, output) => + specificMethodCommand.Handler = CommandHandler.Create((methodName, assemblyPath, output, ci, eval, inc, id) => { + var parameters = new Parameters(ci, eval, inc, id); var assembly = ResolveAssembly(assemblyPath); if (assembly != null) { var method = ResolveMethod(assembly, methodName); if (method != null) { - PostProcess(TestGenerator.Cover(method, output.FullName)); + PostProcess(TestGenerator.Cover(method, parameters, output.FullName)); } } }); diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index b82c063fa..2f5ef75e0 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -6,6 +6,9 @@ open FSharpx.Collections open VSharp module API = + let SetFeatureFlags flags = + FeatureFlags.set flags + let ConfigureSolver solver = SolverInteraction.configureSolver solver let ConfigureSimplifier simplifier = @@ -233,8 +236,8 @@ module API = let Contradicts state condition = let copy = PC.add condition state.pc copy.IsFalse - let PathConditionToSeq (pc : PC.IPathCondition) = pc.ToSeq() - let EmptyPathCondition() = PC.PathCondition() :> PC.IPathCondition + let PathConditionToSeq (pc : IPathCondition) = pc.ToSeq() + let EmptyPathCondition() = PC.create() module Types = let Numeric t = Types.Numeric t @@ -584,7 +587,7 @@ module API = | _ -> internalfailf "constructing string from char array: expected string reference, but got %O" dstRef let ComposeStates state state' = Memory.composeStates state state' - let WLP state (pc' : PC.IPathCondition) = PC.map (Memory.fillHoles state) pc' |> PC.unionWith state.pc + let WLP state (pc' : IPathCondition) = PC.map (Memory.fillHoles state) pc' |> PC.unionWith state.pc let Merge2States (s1 : state) (s2 : state) = Memory.merge2States s1 s2 let Merge2Results (r1, s1 : state) (r2, s2 : state) = Memory.merge2Results (r1, s1) (r2, s2) @@ -612,4 +615,4 @@ module API = module Print = let Dump state = Memory.dump state - let PrintPC (pc : PC.IPathCondition) = pc.ToString() + let PrintPC (pc : IPathCondition) = pc.ToString() diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index eb4131e52..7875289cc 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -5,6 +5,8 @@ open System.Reflection [] module API = + val SetFeatureFlags : featureFlags -> unit + val ConfigureSolver : SolverInteraction.ISolver -> unit val ConfigureSimplifier : IPropositionalSimplifier -> unit val Reset : unit -> unit @@ -18,7 +20,7 @@ module API = val StatedConditionalExecutionAppendResults : (state -> (state -> (term * state -> 'a) -> 'b) -> (state -> (state list -> 'a) -> 'a) -> (state -> (state list -> 'a) -> 'a) -> (state list -> 'a) -> 'b) val GuardedApplyExpression : term -> (term -> term) -> term - val GuardedApplyExpressionWithPC : PC.IPathCondition -> term -> (term -> term) -> term + val GuardedApplyExpressionWithPC : IPathCondition -> term -> (term -> term) -> term val GuardedStatedApplyStatementK : state -> term -> (state -> term -> (term * state -> 'a) -> 'a) -> ((term * state) list -> 'a) -> 'a val GuardedStatedApplyk : (state -> term -> ('item -> 'a) -> 'a) -> state -> term -> ('item list -> 'item list) -> ('item list -> 'a) -> 'a @@ -105,8 +107,8 @@ module API = val AddConstraint : state -> term -> unit val IsFalsePathCondition : state -> bool val Contradicts : state -> term -> bool - val PathConditionToSeq : PC.IPathCondition -> term seq - val EmptyPathCondition : unit -> PC.IPathCondition + val PathConditionToSeq : IPathCondition -> term seq + val EmptyPathCondition : unit -> IPathCondition module Types = val Numeric : System.Type -> symbolicType @@ -286,7 +288,7 @@ module API = // TODO: get rid of all unnecessary stuff below! val ComposeStates : state -> state -> state list - val WLP : state -> PC.IPathCondition -> PC.IPathCondition + val WLP : state -> IPathCondition -> IPathCondition val Merge2States : state -> state -> state list val Merge2Results : term * state -> term * state -> (term * state) list @@ -297,7 +299,7 @@ module API = module Print = val Dump : state -> string - val PrintPC : PC.IPathCondition -> string + val PrintPC : IPathCondition -> string // module Marshalling = // val Unmarshal : state -> obj -> term * state diff --git a/VSharp.SILI.Core/Branching.fs b/VSharp.SILI.Core/Branching.fs new file mode 100644 index 000000000..319d8fa7d --- /dev/null +++ b/VSharp.SILI.Core/Branching.fs @@ -0,0 +1,189 @@ +namespace VSharp.Core + +open System.Collections.Generic +open VSharp +open VSharp.Core.PC + +module internal Branching = + + let private withPc newPc state = { state with pc = newPc } + + let private noNewConstants condition (pc : IPathCondition) = + let pcConstants = HashSet(pc.Constants) + let condConstants = condition |> Seq.singleton |> discoverConstants + pcConstants.IsSupersetOf condConstants + + let private keepDependentWith (pc : IPathCondition) cond = + pc.Fragments + |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) + |> Option.defaultValue pc + + let private executionWithConstraintIndependence tryEval copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let execution thenState elseState condition k = + assert (condition <> True && condition <> False) + thenBranch thenState (fun thenResult -> + elseBranch elseState (fun elseResult -> + merge2Results thenResult elseResult |> k)) + conditionInvocation state (fun (condition, conditionState) -> + let negatedCondition = !!condition + let thenPc = PC.add condition state.pc + let elsePc = PC.add negatedCondition state.pc + if thenPc.IsFalse then + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + elif elsePc.IsFalse then + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + else + let independentThenPc = keepDependentWith thenPc condition + // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond + let independentElsePc = keepDependentWith elsePc negatedCondition + let checkBothBranches () = + conditionState.pc <- independentThenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- independentElsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.model <- Some thenModel.mdl + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState |> withPc elsePc + elseState.model <- Some elseModel.mdl + thenState.pc <- thenPc + execution thenState elseState condition k + if tryEval && noNewConstants condition state.pc then + let evaluatedCondition = state.model.Value.Eval condition + // Current model satisfies new condition, so we can keep it for 'then' branch + if isTrue evaluatedCondition then + let elseState = copy conditionState |> withPc independentElsePc + conditionState.pc <- thenPc + match SolverInteraction.checkSat elseState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + elseState.pc <- elsePc + elseState.model <- Some elseModel.mdl + execution conditionState elseState condition k + // Current model satisfies !condition, so we can keep it for 'else' branch + elif isFalse evaluatedCondition then + let thenState = copy conditionState |> withPc independentThenPc + match SolverInteraction.checkSat thenState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + let elseState = copy conditionState |> withPc elsePc + conditionState.pc <- thenPc + conditionState.model <- Some thenModel.mdl + execution conditionState elseState condition k + else + checkBothBranches() + else + checkBothBranches()) + + let private executionWithoutConstraintIndependence tryEval copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let execution thenState elseState condition k = + assert (condition <> True && condition <> False) + thenBranch thenState (fun thenResult -> + elseBranch elseState (fun elseResult -> + merge2Results thenResult elseResult |> k)) + conditionInvocation state (fun (condition, conditionState) -> + let negatedCondition = !!condition + let thenPc = PC.add condition state.pc + let elsePc = PC.add negatedCondition state.pc + if thenPc.IsFalse then + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + elif elsePc.IsFalse then + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + else + let checkBothBranches () = + conditionState.pc <- thenPc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + __insufficientInformation__ "Unable to witness branch" + | SolverInteraction.SmtSat elseModel -> + conditionState.pc <- elsePc + conditionState.model <- Some elseModel.mdl + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtUnsat _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + conditionState.pc <- elsePc + match SolverInteraction.checkSat conditionState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.model <- Some thenModel.mdl + conditionState.pc <- thenPc + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + conditionState.model <- Some thenModel.mdl + let thenState = conditionState + let elseState = copy conditionState |> withPc elsePc + elseState.model <- Some elseModel.mdl + thenState.pc <- thenPc + execution thenState elseState condition k + if tryEval && noNewConstants condition state.pc then + let evaluatedCondition = state.model.Value.Eval condition + // Current model satisfies new condition, so we can keep it for 'then' branch + if isTrue evaluatedCondition then + let elseState = copy conditionState |> withPc elsePc + conditionState.pc <- thenPc + match SolverInteraction.checkSat elseState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + thenBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat elseModel -> + elseState.pc <- elsePc + elseState.model <- Some elseModel.mdl + execution conditionState elseState condition k + // Current model satisfies !condition, so we can keep it for 'else' branch + elif isFalse evaluatedCondition then + let thenState = copy conditionState |> withPc thenPc + match SolverInteraction.checkSat thenState with + | SolverInteraction.SmtUnsat _ + | SolverInteraction.SmtUnknown _ -> + conditionState.pc <- elsePc + elseBranch conditionState (List.singleton >> k) + | SolverInteraction.SmtSat thenModel -> + let elseState = copy conditionState |> withPc elsePc + conditionState.pc <- thenPc + conditionState.model <- Some thenModel.mdl + execution conditionState elseState condition k + else + checkBothBranches() + else + checkBothBranches()) + + let branch copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = + let isEvalEnabled = FeatureFlags.current.isConditionEvalEnabled + if FeatureFlags.current.isConstraintIndependenceEnabled then + executionWithConstraintIndependence isEvalEnabled copy state conditionInvocation thenBranch elseBranch merge2Results k + else + executionWithoutConstraintIndependence isEvalEnabled copy state conditionInvocation thenBranch elseBranch merge2Results k diff --git a/VSharp.SILI.Core/FeatureFlags.fs b/VSharp.SILI.Core/FeatureFlags.fs new file mode 100644 index 000000000..6fad867c9 --- /dev/null +++ b/VSharp.SILI.Core/FeatureFlags.fs @@ -0,0 +1,18 @@ +namespace VSharp.Core + +type public featureFlags = { + isConstraintIndependenceEnabled : bool + isConditionEvalEnabled : bool + isIncrementalityEnabled : bool +} + +module internal FeatureFlags = + + let mutable current = { + isConstraintIndependenceEnabled = false + isConditionEvalEnabled = false + isIncrementalityEnabled = false + } + + let public set flags = + current <- flags \ No newline at end of file diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index c4af35ca4..bbdfa13fa 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -26,12 +26,12 @@ module internal Memory = let mutable memoryMode = SymbolicMemory - let copy state newPc = + let copy state = let state = match memoryMode with | ConcreteMemory -> ConcreteMemory.deepCopy state | SymbolicMemory -> state - { state with pc = newPc; id = Guid.NewGuid().ToString() } + { state with id = Guid.NewGuid().ToString() } let private isZeroAddress (x : concreteHeapAddress) = x = VectorTime.zero @@ -481,7 +481,7 @@ module internal Memory = match pcs with | [] -> k [] | (pc, v)::pcs -> - let copyState (pc, v) k = f (copy state pc) v k + let copyState (pc, v) k = f { (copy state) with pc = pc } v k Cps.List.mapk copyState pcs (fun results -> state.pc <- pc f state v (fun r -> @@ -492,174 +492,9 @@ module internal Memory = let guardedStatedMap mapper state term = commonGuardedStatedApplyk (fun state term k -> mapper state term |> k) state term id id - - let executionWithConstraintIndependence (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let keepDependentWith (pc : PC.IPathCondition) cond = - pc.Fragments - |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) - |> Option.defaultValue pc - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let negatedCondition = !!condition - let thenPc = PC.add condition state.pc - let elsePc = PC.add negatedCondition state.pc - let independentThenPc = keepDependentWith thenPc condition - // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond - let independentElsePc = keepDependentWith elsePc negatedCondition - if thenPc.IsFalse then - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - elif elsePc.IsFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - else -(* let pcConstants = HashSet(state.pc.Constants) - let condConstants = condition |> Seq.singleton |> discoverConstants - let noNewConstants = pcConstants.IsSupersetOf condConstants*) - let evaluatedCondition = state.model.Value.Eval condition - // Current model satisfies new condition, so we can keep it for 'then' branch - if isTrue evaluatedCondition then - let elseState = copy conditionState independentElsePc - conditionState.pc <- thenPc - match SolverInteraction.checkSat elseState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - elseState.pc <- elsePc - elseState.model <- Some elseModel.mdl - execution conditionState elseState condition k - // Current model satisfies !condition, so we can keep it for 'else' branch - elif isFalse evaluatedCondition then - let thenState = copy conditionState independentThenPc - match SolverInteraction.checkSat thenState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - let elseState = copy conditionState elsePc - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - execution conditionState elseState condition k - else - Logger.trace $"Evaluated condition != False and != True: {evaluatedCondition}" - conditionState.pc <- independentThenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- elsePc - conditionState.model <- Some elseModel.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.model <- Some thenModel.mdl - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState elsePc - elseState.model <- Some elseModel.mdl - thenState.pc <- thenPc - execution thenState elseState condition k) - - let executionWithoutConstraintIndependence (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let negatedCondition = !!condition - let thenPc = PC.add condition state.pc - let elsePc = PC.add negatedCondition state.pc - if thenPc.IsFalse then - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - elif elsePc.IsFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - else -(* let pcConstants = HashSet(state.pc.Constants) - let condConstants = condition |> Seq.singleton |> discoverConstants - let noNewConstants = pcConstants.IsSupersetOf condConstants*) - let evaluatedCondition = state.model.Value.Eval condition - // Current model satisfies new condition, so we can keep it for 'then' branch - if isTrue evaluatedCondition then - let elseState = copy conditionState elsePc - conditionState.pc <- thenPc - match SolverInteraction.checkSat elseState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - elseState.pc <- elsePc - elseState.model <- Some elseModel.mdl - execution conditionState elseState condition k - // Current model satisfies !condition, so we can keep it for 'else' branch - elif isFalse evaluatedCondition then - let thenState = copy conditionState thenPc - match SolverInteraction.checkSat thenState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - let elseState = copy conditionState elsePc - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - execution conditionState elseState condition k - else - Logger.trace $"Evaluated condition != False and != True: {evaluatedCondition}" - conditionState.pc <- thenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- elsePc - conditionState.model <- Some elseModel.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- elsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.model <- Some thenModel.mdl - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState elsePc - elseState.model <- Some elseModel.mdl - thenState.pc <- thenPc - execution thenState elseState condition k) - let commonStatedConditionalExecutionk = - executionWithoutConstraintIndependence + let commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k = + Branching.branch copy state conditionInvocation thenBranch elseBranch merge2Results k let statedConditionalExecutionWithMergek state conditionInvocation thenBranch elseBranch k = commonStatedConditionalExecutionk state conditionInvocation thenBranch elseBranch merge2Results k diff --git a/VSharp.SILI.Core/Merging.fs b/VSharp.SILI.Core/Merging.fs index 4108e88dd..01ed8cb76 100644 --- a/VSharp.SILI.Core/Merging.fs +++ b/VSharp.SILI.Core/Merging.fs @@ -102,7 +102,7 @@ module internal Merging = let guardedApplyk f term k = commonGuardedApplyk f term merge k let guardedApply f term = guardedApplyk (Cps.ret f) term id - let commonGuardedMapkWithPC (pc : PC.IPathCondition) mapper gvs merge k = + let commonGuardedMapkWithPC (pc : IPathCondition) mapper gvs merge k = let foldFunc gvs (g, v) k = let pc' = PC.add g pc if pc'.IsFalse then k gvs diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 308b9b8a1..55cfc41b5 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -3,19 +3,23 @@ open VSharp open VSharp.Utils open System.Collections.Generic -module public PC = - - type IPathCondition = - abstract Add : term -> unit - abstract Copy : unit -> IPathCondition - abstract ToSeq : unit -> term seq - abstract UnionWith : IPathCondition -> IPathCondition - abstract Map : (term -> term) -> IPathCondition - abstract IsEmpty : bool - abstract IsFalse : bool - abstract Fragments : IPathCondition seq +type public IPathCondition = + abstract Add : term -> unit + abstract Copy : unit -> IPathCondition + abstract ToSeq : unit -> term seq + abstract UnionWith : IPathCondition -> IPathCondition + abstract Map : (term -> term) -> IPathCondition + abstract IsEmpty : bool + abstract IsFalse : bool + abstract Fragments : IPathCondition seq + abstract Constants : term seq + +module internal PC = - type public PathCondition private (constraints : HashSet, isFalse : bool) = + /// + /// Naive path condition implementation which maintains a single set of constraints + /// + type private PathCondition private (constraints : HashSet, isFalse : bool) = let mutable isFalse = isFalse @@ -61,6 +65,8 @@ module public PC = member this.IsFalse = isFalse member this.Fragments = Seq.singleton this + + member this.Constants = seq constraints |> Seq.map Seq.singleton |> Seq.collect discoverConstants type private node = | Tail of term * term pset @@ -73,9 +79,10 @@ module public PC = | Node(constant) -> constant | Empty -> invalidOp "Cannot unwrap empty node" - (* - Path condition maintains independent subsets of constants and constraints ("constraint independence") - + /// + /// Path condition implementation which maintains independent subsets of constants and constraints ("constraint independence") + /// + (* constants -- dictionary used as union-find structure for constants. Constants of one subset are cyclically mapping to each other. There is only one node.Tail in subset and it is the representative element of the subset. Tail also contains the constraints corresponding to the constants subset @@ -86,7 +93,7 @@ module public PC = isFalse -- flag used to determine if the PC is false trivially (i. e. c an !c were added to it). Invariant: PC doesn't contain True or False as elements. *) - type public IndependentPathCondition private (constants : Dictionary, constraints : HashSet, isFalse : bool) = + type private IndependentPathCondition private (constants : Dictionary, constraints : HashSet, isFalse : bool) = let mutable isFalse = isFalse @@ -210,7 +217,7 @@ module public PC = | _ -> () | _ -> () - new() = + internal new() = IndependentPathCondition(Dictionary(), HashSet(), false) override this.ToString() = @@ -265,6 +272,8 @@ module public PC = | _ -> None Seq.choose getSubsetByRepresentative constants.Values |> Seq.cast + + member this.Constants = constants.Keys let public add newConstraint (pc : IPathCondition) = let copy = pc.Copy() @@ -276,3 +285,9 @@ module public PC = let public map mapper (pc : IPathCondition) = pc.Map mapper let public unionWith anotherPc (pc : IPathCondition) = pc.UnionWith anotherPc + + let public create () : IPathCondition = + if FeatureFlags.current.isConstraintIndependenceEnabled then + IndependentPathCondition() + else + PathCondition() diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index e382a83dc..67ff6acba 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -59,5 +59,9 @@ module public SolverInteraction = s.CheckAssumptions ctx model conditions | None -> SmtUnknown "Solver not configured" - let checkSat = - checkSatIncrementally// TODO: need to solve types here? #do \ No newline at end of file + let checkSat state = + // TODO: need to solve types here? #do + if FeatureFlags.current.isIncrementalityEnabled then + checkSatIncrementally state + else + checkSatPlainly state \ No newline at end of file diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 4b04c405d..7118c9b58 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -117,7 +117,7 @@ and [] state = { id : string - mutable pc : PC.IPathCondition + mutable pc : IPathCondition mutable evaluationStack : evaluationStack mutable stack : callStack // Arguments and local variables mutable stackBuffers : pdict // Buffers allocated via stackAlloc @@ -147,7 +147,7 @@ and module public State = let makeEmpty modelState = { id = Guid.NewGuid().ToString() - pc = PC.PathCondition() + pc = PC.create() evaluationStack = EvaluationStack.empty exceptionsRegister = NoException stack = CallStack.empty diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index 1f4ef5d78..0005b54fb 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -17,6 +17,7 @@ + @@ -35,6 +36,7 @@ + diff --git a/VSharp.SILI/Statistics.fs b/VSharp.SILI/Statistics.fs index d04b524dc..9f5a3ac32 100644 --- a/VSharp.SILI/Statistics.fs +++ b/VSharp.SILI/Statistics.fs @@ -12,7 +12,7 @@ open VSharp.Utils open CilStateOperations open ipOperations -type pob = {loc : codeLocation; lvl : uint; pc : PC.IPathCondition} +type pob = {loc : codeLocation; lvl : uint; pc : IPathCondition} with override x.ToString() = sprintf "loc = %O; lvl = %d; pc = %s" x.loc x.lvl (Print.PrintPC x.pc) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 2885d7aab..ef37b74a4 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -11,6 +11,7 @@ using NUnit.Framework.Internal; using NUnit.Framework.Internal.Builders; using NUnit.Framework.Internal.Commands; +using VSharp.Core; using VSharp.Interpreter.IL; using VSharp.Solver; @@ -96,6 +97,8 @@ public TestSvmCommand( private TestResult Explore(TestExecutionContext context) { Core.API.ConfigureSolver(SolverPool.mkSolver()); + Core.API.SetFeatureFlags(new featureFlags(true, true, true)); + var methodInfo = innerCommand.Test.Method.MethodInfo; try { @@ -137,9 +140,6 @@ private TestResult Explore(TestExecutionContext context) { context.CurrentResult.SetResult(ResultState.Success); } - - Stopwatch.saveMeasurements(methodInfo.Name, unitTests.UnitTestsCount); - Stopwatch.clear(); } catch (Exception e) { diff --git a/VSharp.Utils/Logger.fs b/VSharp.Utils/Logger.fs index 3887b653f..b37655a93 100644 --- a/VSharp.Utils/Logger.fs +++ b/VSharp.Utils/Logger.fs @@ -20,9 +20,10 @@ module Logger = | _ -> "Unknown" let private writeLineString vLevel message = - let res = sprintf "[%s] [%A] %s" (LevelToString vLevel) DateTime.Now message + () +(* let res = sprintf "[%s] [%A] %s" (LevelToString vLevel) DateTime.Now message current_text_writer.WriteLine(res) - current_text_writer.Flush() + current_text_writer.Flush()*) let public printLog vLevel format = Printf.ksprintf (fun message -> if current_log_level >= vLevel then writeLineString vLevel message) format diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index 8fc75667c..33f5fc3ad 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -16,7 +16,7 @@ module Stopwatch = type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } type private csvRecord = { - commitHash: string + runId: string dateTime: string caseName: string tag: string @@ -81,11 +81,10 @@ module Stopwatch = /// Saves all current measurement results to .csv file. If the file already exists, appends lines /// /// Additional tag given to the saved measurements - /// Number of tests generated during the run - let public saveMeasurements caseName testsGenerated = + /// Number of tests generated during the run + let public saveMeasurements runId caseName testsGenerated = stopAll() - - let commitHash = getGitCommitHash() + let currentDateTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") let records = @@ -93,7 +92,7 @@ module Stopwatch = |> Seq.map (|KeyValue|) |> Seq.map (fun (tag, m) -> { - commitHash = commitHash + runId = runId dateTime = currentDateTime caseName = caseName tag = tag @@ -102,7 +101,7 @@ module Stopwatch = totalMs = m.stopwatch.ElapsedMilliseconds testsGenerated = testsGenerated } - ) + ) let targetPath = Path.Combine(csvPath, csvFilename) @@ -119,4 +118,4 @@ module Stopwatch = /// /// Clears all current measurements /// - let public clear () = measurements.Clear() + let public clear () = measurements.Clear() diff --git a/VSharp.Utils/UnitTests.fs b/VSharp.Utils/UnitTests.fs index 70f01f2b6..e5a62164e 100644 --- a/VSharp.Utils/UnitTests.fs +++ b/VSharp.Utils/UnitTests.fs @@ -32,8 +32,8 @@ type UnitTests(outputDir : string) = member x.GenerateTest (test : UnitTest) = testNumber <- testNumber + 1u - if test.StateId.IsSome then - TaggedLogger.saveLog test.StateId.Value $"%s{currentDir.FullName}%c{Path.DirectorySeparatorChar}info%s{testNumber.ToString()}.txt" + (*if test.StateId.IsSome then + TaggedLogger.saveLog test.StateId.Value $"%s{currentDir.FullName}%c{Path.DirectorySeparatorChar}info%s{testNumber.ToString()}.txt"*) generateTest test ("test" + testNumber.ToString()) member x.GenerateError (test : UnitTest) = From a9b40c3996bca17b7eeee177e62903cbfaa8624a Mon Sep 17 00:00:00 2001 From: mxprshn Date: Thu, 2 Jun 2022 19:56:46 +0300 Subject: [PATCH 75/88] [fix] Fix CLI arguments, disable logging and time measurement, style fixes --- VSharp.API/Parameters.cs | 18 ----- VSharp.API/SvmOptions.cs | 24 ++++++ VSharp.API/VSharp.cs | 78 +++++++++---------- VSharp.Runner/RunnerProgram.cs | 104 +++++++++++++++----------- VSharp.SILI.Core/Branching.fs | 3 +- VSharp.SILI.Core/FeatureFlags.fs | 6 +- VSharp.SILI.Core/PathCondition.fs | 11 ++- VSharp.SILI.Core/SolverInteraction.fs | 3 +- VSharp.SILI.Core/State.fs | 2 - VSharp.Solver/Z3.fs | 4 +- VSharp.Test/IntegrationTests.cs | 11 +-- VSharp.Utils/Collections.fs | 4 + VSharp.Utils/Logger.fs | 7 +- VSharp.Utils/Stopwatch.fs | 7 +- VSharp.Utils/TaggedLogger.fs | 3 +- VSharp.Utils/UnitTests.fs | 2 - 16 files changed, 152 insertions(+), 135 deletions(-) delete mode 100644 VSharp.API/Parameters.cs create mode 100644 VSharp.API/SvmOptions.cs diff --git a/VSharp.API/Parameters.cs b/VSharp.API/Parameters.cs deleted file mode 100644 index f7708fb1b..000000000 --- a/VSharp.API/Parameters.cs +++ /dev/null @@ -1,18 +0,0 @@ -using VSharp.Core; - -namespace VSharp; - -public readonly record struct Parameters( - bool IsConstraintIndependenceEnabled, - bool IsConditionEvalEnabled, - bool IsIncrementalityEnabled, - string RunId = "" -) -{ - public featureFlags GetFeatureFlags() => - new featureFlags( - isConstraintIndependenceEnabled: IsConstraintIndependenceEnabled, - isConditionEvalEnabled: IsConditionEvalEnabled, - isIncrementalityEnabled: IsIncrementalityEnabled - ); -} \ No newline at end of file diff --git a/VSharp.API/SvmOptions.cs b/VSharp.API/SvmOptions.cs new file mode 100644 index 000000000..2625b035e --- /dev/null +++ b/VSharp.API/SvmOptions.cs @@ -0,0 +1,24 @@ +using VSharp.Core; + +namespace VSharp +{ + /// + /// Advanced symbolic virtual machine options. + /// + /// If true, independent constraint sets are maintained (constraint independence optimization). + /// If true, branch condition is evaluated with current model to avoid extra SMT solver queries. + /// If true, SMT solver works in incremental mode. + public readonly record struct SvmOptions( + bool IsConstraintIndependenceEnabled = false, + bool IsConditionEvaluationEnabled = false, + bool IsSolverIncrementalityEnabled = false + ) + { + internal featureFlags GetFeatureFlags() => + new featureFlags( + isConstraintIndependenceEnabled: IsConstraintIndependenceEnabled, + isConditionEvaluationEnabled: IsConditionEvaluationEnabled, + isIncrementalityEnabled: IsSolverIncrementalityEnabled + ); + } +} diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index d1bdafd6c..bb69d81e5 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -3,8 +3,6 @@ using System.IO; using System.Linq; using System.Reflection; -using Microsoft.FSharp.Core; -using VSharp.Core; using VSharp.Interpreter.IL; using VSharp.Solver; @@ -73,7 +71,7 @@ public void GenerateReport(TextWriter writer) public static class TestGenerator { - private static Statistics StartExploration(List methods, string resultsFolder, Parameters parameters, string[] mainArguments = null) + private static Statistics StartExploration(List methods, string resultsFolder, SvmOptions svmOptions, string[] mainArguments = null) { var recThreshold = 0u; var options = @@ -82,31 +80,21 @@ private static Statistics StartExploration(List methods, string resu SILI explorer = new SILI(options); UnitTests unitTests = new UnitTests(resultsFolder); Core.API.ConfigureSolver(SolverPool.mkSolver()); - Core.API.SetFeatureFlags(parameters.GetFeatureFlags()); - - var totalTestsGenerated = 0u; + Core.API.SetFeatureFlags(svmOptions.GetFeatureFlags()); + // TODO: single solver instance for multiple methods causes problems in incrementality mode foreach (var method in methods) { - Stopwatch.runMeasuringTime("total", FuncConvert.FromAction(() => - { - if (method == method.Module.Assembly.EntryPoint) - { - explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, - e => throw e); - } - else - { - explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, - e => throw e); - } - } - )); - - Stopwatch.saveMeasurements(parameters.RunId, method.Name, unitTests.UnitTestsCount - totalTestsGenerated); - Stopwatch.clear(); - - totalTestsGenerated = unitTests.UnitTestsCount; + if (method == method.Module.Assembly.EntryPoint) + { + explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, + e => throw e); + } + else + { + explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, + e => throw e); + } } var statistics = new Statistics(explorer.Statistics.CurrentExplorationTime, unitTests.TestDirectory, @@ -126,11 +114,12 @@ private static bool Reproduce(DirectoryInfo testDir) /// /// Type to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// Summary of tests generation process. - public static Statistics Cover(MethodBase method, Parameters parameters, string outputDirectory = "") + public static Statistics Cover(MethodBase method, string outputDirectory = "", SvmOptions svmOptions = new()) { List methods = new List {method}; - return StartExploration(methods, outputDirectory, parameters); + return StartExploration(methods, outputDirectory, svmOptions); } /// @@ -138,9 +127,10 @@ public static Statistics Cover(MethodBase method, Parameters parameters, string /// /// Type to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// Summary of tests generation process. /// Thrown if specified class does not contain public methods. - public static Statistics Cover(Type type, Parameters parameters, string outputDirectory = "") + public static Statistics Cover(Type type, string outputDirectory = "", SvmOptions svmOptions = new()) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; @@ -155,7 +145,7 @@ public static Statistics Cover(Type type, Parameters parameters, string outputDi throw new ArgumentException("I've not found any public method of class " + type.FullName); } - return StartExploration(methods, outputDirectory, parameters); + return StartExploration(methods, outputDirectory, svmOptions); } /// @@ -163,10 +153,11 @@ public static Statistics Cover(Type type, Parameters parameters, string outputDi /// /// Assembly to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// Summary of tests generation process. /// Thrown if no public methods found in assembly. /// - public static Statistics Cover(Assembly assembly, Parameters parameters, string outputDirectory = "") + public static Statistics Cover(Assembly assembly, string outputDirectory = "", SvmOptions svmOptions = new()) { List methods; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | @@ -188,7 +179,7 @@ public static Statistics Cover(Assembly assembly, Parameters parameters, string throw new ArgumentException("I've not found any public method in assembly"); } - return StartExploration(methods, outputDirectory, parameters); + return StartExploration(methods, outputDirectory, svmOptions); } /// @@ -197,10 +188,11 @@ public static Statistics Cover(Assembly assembly, Parameters parameters, string /// Assembly to be covered with tests. /// Command line arguments of entry point /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// Summary of tests generation process. /// Thrown if assembly does not contain entry point. /// - public static Statistics Cover(Assembly assembly, Parameters parameters, string[] args, string outputDirectory = "") + public static Statistics Cover(Assembly assembly, string[] args, string outputDirectory = "", SvmOptions svmOptions = new()) { List methods; var entryPoint = assembly.EntryPoint; @@ -210,7 +202,7 @@ public static Statistics Cover(Assembly assembly, Parameters parameters, string[ } methods = new List { entryPoint }; - return StartExploration(methods, outputDirectory, parameters, args); + return StartExploration(methods, outputDirectory, svmOptions, args); } /// @@ -218,10 +210,11 @@ public static Statistics Cover(Assembly assembly, Parameters parameters, string[ /// /// Type to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// True if all generated tests have passed. - public static bool CoverAndRun(MethodBase method, Parameters parameters, string outputDirectory = "") + public static bool CoverAndRun(MethodBase method, string outputDirectory = "", SvmOptions svmOptions = new()) { - var stats = Cover(method, parameters, outputDirectory); + var stats = Cover(method, outputDirectory, svmOptions); return Reproduce(stats.OutputDir); } @@ -230,11 +223,12 @@ public static bool CoverAndRun(MethodBase method, Parameters parameters, string /// /// Type to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// True if all generated tests have passed. /// Thrown if specified class does not contain public methods. - public static bool CoverAndRun(Type type, Parameters parameters, string outputDirectory = "") + public static bool CoverAndRun(Type type, string outputDirectory = "", SvmOptions svmOptions = new()) { - var stats = Cover(type, parameters, outputDirectory); + var stats = Cover(type, outputDirectory, svmOptions); return Reproduce(stats.OutputDir); } @@ -243,12 +237,13 @@ public static bool CoverAndRun(Type type, Parameters parameters, string outputDi /// /// Assembly to be covered with tests. /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// True if all generated tests have passed. /// Thrown if no public methods found in assembly. /// - public static bool CoverAndRun(Assembly assembly, Parameters parameters, string outputDirectory = "") + public static bool CoverAndRun(Assembly assembly, string outputDirectory = "", SvmOptions svmOptions = new()) { - var stats = Cover(assembly, parameters, outputDirectory); + var stats = Cover(assembly, outputDirectory, svmOptions); return Reproduce(stats.OutputDir); } @@ -258,11 +253,12 @@ public static bool CoverAndRun(Assembly assembly, Parameters parameters, string /// Assembly to be covered with tests. /// Command line arguments of entry point /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Advanced symbolic virtual machine options. /// True if all generated tests have passed. /// Thrown if assembly does not contain entry point. - public static bool CoverAndRun(Assembly assembly, Parameters parameters, string[] args, string outputDirectory = "") + public static bool CoverAndRun(Assembly assembly, string[] args, string outputDirectory = "", SvmOptions svmOptions = new()) { - var stats = Cover(assembly, parameters, args, outputDirectory); + var stats = Cover(assembly, args, outputDirectory, svmOptions); return Reproduce(stats.OutputDir); } diff --git a/VSharp.Runner/RunnerProgram.cs b/VSharp.Runner/RunnerProgram.cs index 575924dec..8bc6ac94f 100644 --- a/VSharp.Runner/RunnerProgram.cs +++ b/VSharp.Runner/RunnerProgram.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.IO; @@ -122,11 +121,10 @@ public static int Main(string[] args) var unknownArgsOption = new Option("--unknown-args", description: "Force engine to generate various input console arguments"); - var constraintIndependenceOption = new Option("--cind"); - var evalConditionOption = new Option("--eval-cond"); - var incrementalityOption = new Option("--incrementality"); - var runIdOption = new Option("--runId"); - + var constraintIndependenceOption = new Option("--c-independence", description: "Advanced: maintain independent constraint sets (constraint independence optimization)"); + var conditionEvaluationOption = new Option("--eval-condition", description: "Advanced: evaluate branch condition with current model to avoid extra SMT solver queries"); + var incrementalityOption = new Option("--incrementality", description: "Advanced: enable SMT solver incremental mode"); + var rootCommand = new RootCommand(); var entryPointCommand = @@ -136,23 +134,28 @@ public static int Main(string[] args) entryPointCommand.AddArgument(concreteArguments); entryPointCommand.AddGlobalOption(outputOption); entryPointCommand.AddOption(unknownArgsOption); + entryPointCommand.AddOption(constraintIndependenceOption); + entryPointCommand.AddOption(conditionEvaluationOption); + entryPointCommand.AddOption(incrementalityOption); var allPublicMethodsCommand = new Command("--all-public-methods", "Generate unit tests for all public methods of all public classes of assembly"); rootCommand.AddCommand(allPublicMethodsCommand); allPublicMethodsCommand.AddArgument(assemblyPathArgument); allPublicMethodsCommand.AddGlobalOption(outputOption); + allPublicMethodsCommand.AddOption(constraintIndependenceOption); + allPublicMethodsCommand.AddOption(conditionEvaluationOption); + allPublicMethodsCommand.AddOption(incrementalityOption); var publicMethodsOfClassCommand = - new Command("public-methods-of-class", "Generate unit tests for all public methods of specified class"); + new Command("--public-methods-of-class", "Generate unit tests for all public methods of specified class"); rootCommand.AddCommand(publicMethodsOfClassCommand); var classArgument = new Argument("class-name"); publicMethodsOfClassCommand.AddArgument(classArgument); publicMethodsOfClassCommand.AddArgument(assemblyPathArgument); publicMethodsOfClassCommand.AddGlobalOption(outputOption); - publicMethodsOfClassCommand.AddOption(runIdOption); publicMethodsOfClassCommand.AddOption(constraintIndependenceOption); - publicMethodsOfClassCommand.AddOption(evalConditionOption); + publicMethodsOfClassCommand.AddOption(conditionEvaluationOption); publicMethodsOfClassCommand.AddOption(incrementalityOption); var specificMethodCommand = @@ -162,51 +165,66 @@ public static int Main(string[] args) specificMethodCommand.AddArgument(methodArgument); specificMethodCommand.AddArgument(assemblyPathArgument); specificMethodCommand.AddGlobalOption(outputOption); + specificMethodCommand.AddOption(constraintIndependenceOption); + specificMethodCommand.AddOption(conditionEvaluationOption); + specificMethodCommand.AddOption(incrementalityOption); rootCommand.Description = "Symbolic execution engine for .NET"; - entryPointCommand.Handler = CommandHandler.Create((assemblyPath, args, output, unknownArgs, ci, eval, inc, id) => - { - var parameters = new Parameters(ci, eval, inc, id); - var assembly = ResolveAssembly(assemblyPath); - if (unknownArgs) - args = null; - if (assembly != null) - PostProcess(TestGenerator.Cover(assembly, parameters, args, output.FullName)); - }); - allPublicMethodsCommand.Handler = CommandHandler.Create((assemblyPath, output, ci, eval, inc, id) => - { - var parameters = new Parameters(ci, eval, inc, id); - var assembly = ResolveAssembly(assemblyPath); - if (assembly != null) - PostProcess(TestGenerator.Cover(assembly, parameters, output.FullName)); - }); - publicMethodsOfClassCommand.Handler = CommandHandler.Create((className, assemblyPath, output, runId, ci, eval, inc) => - { - var parameters = new Parameters(ci, eval, inc, runId); - var assembly = ResolveAssembly(assemblyPath); - if (assembly != null) + entryPointCommand.Handler = CommandHandler.Create + ( + (assemblyPath, args, output, unknownArgs, cIndependence, evalCondition, incrementality) => { - var type = ResolveType(assembly, className); - if (type != null) + var options = new SvmOptions(cIndependence, evalCondition, incrementality); + var assembly = ResolveAssembly(assemblyPath); + if (unknownArgs) + args = null; + if (assembly != null) + PostProcess(TestGenerator.Cover(assembly, args, output.FullName, options)); + } + ); + allPublicMethodsCommand.Handler = CommandHandler.Create + ( + (assemblyPath, output, cIndependence, evalCondition, incrementality) => + { + var options = new SvmOptions(cIndependence, evalCondition, incrementality); + var assembly = ResolveAssembly(assemblyPath); + if (assembly != null) + PostProcess(TestGenerator.Cover(assembly, output.FullName, options)); + } + ); + publicMethodsOfClassCommand.Handler = CommandHandler.Create + ( + (className, assemblyPath, output, cIndependence, evalCondition, incrementality) => + { + var options = new SvmOptions(cIndependence, evalCondition, incrementality); + var assembly = ResolveAssembly(assemblyPath); + if (assembly != null) { - PostProcess(TestGenerator.Cover(type, parameters, output.FullName)); + var type = ResolveType(assembly, className); + if (type != null) + { + PostProcess(TestGenerator.Cover(type, output.FullName, options)); + } } } - }); - specificMethodCommand.Handler = CommandHandler.Create((methodName, assemblyPath, output, ci, eval, inc, id) => - { - var parameters = new Parameters(ci, eval, inc, id); - var assembly = ResolveAssembly(assemblyPath); - if (assembly != null) + ); + specificMethodCommand.Handler = CommandHandler.Create + ( + (methodName, assemblyPath, output, cIndependence, evalCondition, incrementality) => { - var method = ResolveMethod(assembly, methodName); - if (method != null) + var options = new SvmOptions(cIndependence, evalCondition, incrementality); + var assembly = ResolveAssembly(assemblyPath); + if (assembly != null) { - PostProcess(TestGenerator.Cover(method, parameters, output.FullName)); + var method = ResolveMethod(assembly, methodName); + if (method != null) + { + PostProcess(TestGenerator.Cover(method, output.FullName, options)); + } } } - }); + ); return rootCommand.InvokeAsync(args).Result; } diff --git a/VSharp.SILI.Core/Branching.fs b/VSharp.SILI.Core/Branching.fs index 319d8fa7d..9e6a84c04 100644 --- a/VSharp.SILI.Core/Branching.fs +++ b/VSharp.SILI.Core/Branching.fs @@ -2,7 +2,6 @@ namespace VSharp.Core open System.Collections.Generic open VSharp -open VSharp.Core.PC module internal Branching = @@ -182,7 +181,7 @@ module internal Branching = checkBothBranches()) let branch copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let isEvalEnabled = FeatureFlags.current.isConditionEvalEnabled + let isEvalEnabled = FeatureFlags.current.isConditionEvaluationEnabled if FeatureFlags.current.isConstraintIndependenceEnabled then executionWithConstraintIndependence isEvalEnabled copy state conditionInvocation thenBranch elseBranch merge2Results k else diff --git a/VSharp.SILI.Core/FeatureFlags.fs b/VSharp.SILI.Core/FeatureFlags.fs index 6fad867c9..582b2df53 100644 --- a/VSharp.SILI.Core/FeatureFlags.fs +++ b/VSharp.SILI.Core/FeatureFlags.fs @@ -2,7 +2,7 @@ namespace VSharp.Core type public featureFlags = { isConstraintIndependenceEnabled : bool - isConditionEvalEnabled : bool + isConditionEvaluationEnabled : bool isIncrementalityEnabled : bool } @@ -10,9 +10,9 @@ module internal FeatureFlags = let mutable current = { isConstraintIndependenceEnabled = false - isConditionEvalEnabled = false + isConditionEvaluationEnabled = false isIncrementalityEnabled = false } let public set flags = - current <- flags \ No newline at end of file + current <- flags diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 55cfc41b5..f7b61ac78 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -1,6 +1,5 @@ namespace VSharp.Core open VSharp -open VSharp.Utils open System.Collections.Generic type public IPathCondition = @@ -31,7 +30,10 @@ module internal PC = PathCondition(HashSet(), false) override this.ToString() = - Seq.map toString constraints |> Seq.sort |> join " /\ " + if (this :> IPathCondition).IsEmpty then + "true" + else + Seq.map (fun c -> $"({c})") constraints |> join " /\ " interface IPathCondition with @@ -221,7 +223,10 @@ module internal PC = IndependentPathCondition(Dictionary(), HashSet(), false) override this.ToString() = - Seq.map toString constraints |> Seq.sort |> join " /\ " + if (this :> IPathCondition).IsEmpty then + "true" + else + Seq.map (fun c -> $"({c})") constraints |> join " /\ " interface IPathCondition with diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 67ff6acba..f0d8ef582 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -1,6 +1,5 @@ namespace VSharp.Core -open System open System.Collections.Generic open FSharpx.Collections open VSharp @@ -64,4 +63,4 @@ module public SolverInteraction = if FeatureFlags.current.isIncrementalityEnabled then checkSatIncrementally state else - checkSatPlainly state \ No newline at end of file + checkSatPlainly state diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 7118c9b58..b53be6299 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -2,7 +2,6 @@ namespace VSharp.Core open System open System.Collections.Generic -open System.Reflection open VSharp open VSharp.Core.Types.Constructor open VSharp.Utils @@ -169,4 +168,3 @@ module public State = model = Option.bind (fun state -> Some {subst = Dictionary<_,_>(); state = state; complete = true}) modelState } - diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 61107400f..941d61c6f 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -802,7 +802,7 @@ module internal Z3 = yield query.expr } |> Array.ofSeq // let pathAtoms = addSoftConstraints q.lvl - let result = Stopwatch.runMeasuringTime "Z3_check" (fun _ -> optCtx.Check assumptions) + let result = optCtx.Check assumptions match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model @@ -873,7 +873,7 @@ module internal Z3 = let names = boolConsts |> Seq.map (fun c -> c.ToString()) let amp = " & " printLog Trace $"SOLVER: check: {join amp names}" - let result = Stopwatch.runMeasuringTime "Z3_check_assumptions" (fun _ -> optCtx.Check boolConsts) + let result = optCtx.Check boolConsts match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index ef37b74a4..37f108271 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -1,11 +1,10 @@ using System; -using System.IO; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; using System.Threading; -using Microsoft.FSharp.Core; using NUnit.Framework; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -97,7 +96,7 @@ public TestSvmCommand( private TestResult Explore(TestExecutionContext context) { Core.API.ConfigureSolver(SolverPool.mkSolver()); - Core.API.SetFeatureFlags(new featureFlags(true, true, true)); + Core.API.SetFeatureFlags(new featureFlags(false, false, false)); var methodInfo = innerCommand.Test.Method.MethodInfo; try @@ -111,11 +110,7 @@ private TestResult Explore(TestExecutionContext context) SILI explorer = new SILI(_options); UnitTests unitTests = new UnitTests(Directory.GetCurrentDirectory()); - Stopwatch.runMeasuringTime("total", FuncConvert.FromAction(() => - { - explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); - } - )); + explorer.InterpretIsolated(methodInfo, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); if (unitTests.UnitTestsCount == 0 && unitTests.ErrorsCount == 0 && explorer.Statistics.IncompleteStates.Count == 0) { diff --git a/VSharp.Utils/Collections.fs b/VSharp.Utils/Collections.fs index ae72a81aa..707558890 100644 --- a/VSharp.Utils/Collections.fs +++ b/VSharp.Utils/Collections.fs @@ -31,6 +31,10 @@ module public Seq = lenProd <- lenProd * lengths.[i] Array.mapFold detachOne (idx, lenProd) (Array.init lengths.Length id) |> fst + /// + /// Splits sequence into two sequences: with elements which satisfy condition and with those + /// which don't + /// let splitBy condition seq = let grouped = Seq.groupBy (fun element -> condition element) seq match (Seq.tryFind (fun (value, _) -> value) grouped), diff --git a/VSharp.Utils/Logger.fs b/VSharp.Utils/Logger.fs index b37655a93..3f3b6809e 100644 --- a/VSharp.Utils/Logger.fs +++ b/VSharp.Utils/Logger.fs @@ -8,7 +8,7 @@ module Logger = let Info = 3 let Trace = 4 - let mutable current_log_level = Trace + let mutable current_log_level = Error let mutable current_text_writer = Console.Out let public ConfigureWriter writer = current_text_writer <- writer @@ -20,10 +20,9 @@ module Logger = | _ -> "Unknown" let private writeLineString vLevel message = - () -(* let res = sprintf "[%s] [%A] %s" (LevelToString vLevel) DateTime.Now message + let res = sprintf "[%s] [%A] %s" (LevelToString vLevel) DateTime.Now message current_text_writer.WriteLine(res) - current_text_writer.Flush()*) + current_text_writer.Flush() let public printLog vLevel format = Printf.ksprintf (fun message -> if current_log_level >= vLevel then writeLineString vLevel message) format diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index 33f5fc3ad..45146f7ea 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -16,7 +16,7 @@ module Stopwatch = type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } type private csvRecord = { - runId: string + commitHash: string dateTime: string caseName: string tag: string @@ -82,17 +82,18 @@ module Stopwatch = /// /// Additional tag given to the saved measurements /// Number of tests generated during the run - let public saveMeasurements runId caseName testsGenerated = + let public saveMeasurements caseName testsGenerated = stopAll() let currentDateTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") + let commitHash = getGitCommitHash() let records = measurements |> Seq.map (|KeyValue|) |> Seq.map (fun (tag, m) -> { - runId = runId + commitHash = commitHash dateTime = currentDateTime caseName = caseName tag = tag diff --git a/VSharp.Utils/TaggedLogger.fs b/VSharp.Utils/TaggedLogger.fs index 0f0017d61..ea5b98785 100644 --- a/VSharp.Utils/TaggedLogger.fs +++ b/VSharp.Utils/TaggedLogger.fs @@ -1,6 +1,5 @@ namespace VSharp -open System open System.Collections.Generic open System.IO open System.Text @@ -36,5 +35,5 @@ module TaggedLogger = /// Path of the file to save let public saveLog tag path = if logs.ContainsKey tag then - let log = logs[tag].ToString() + let log = logs.[tag].ToString() File.WriteAllText(path, log) diff --git a/VSharp.Utils/UnitTests.fs b/VSharp.Utils/UnitTests.fs index e5a62164e..5f7848453 100644 --- a/VSharp.Utils/UnitTests.fs +++ b/VSharp.Utils/UnitTests.fs @@ -32,8 +32,6 @@ type UnitTests(outputDir : string) = member x.GenerateTest (test : UnitTest) = testNumber <- testNumber + 1u - (*if test.StateId.IsSome then - TaggedLogger.saveLog test.StateId.Value $"%s{currentDir.FullName}%c{Path.DirectorySeparatorChar}info%s{testNumber.ToString()}.txt"*) generateTest test ("test" + testNumber.ToString()) member x.GenerateError (test : UnitTest) = From dd65e89b42d59d223088969c7e0ecaf733c04d8b Mon Sep 17 00:00:00 2001 From: mxprshn Date: Fri, 15 Jul 2022 23:59:50 +0300 Subject: [PATCH 76/88] [style] Style fixes --- VSharp.SILI.Core/API.fs | 5 +- VSharp.SILI.Core/API.fsi | 5 +- VSharp.SILI.Core/Copying.fs | 3 +- VSharp.SILI.Core/Memory.fs | 4 +- VSharp.SILI.Core/PathCondition.fs | 109 +++++++++++--------------- VSharp.SILI.Core/SolverInteraction.fs | 20 ++--- VSharp.SILI.Core/State.fs | 7 +- VSharp.SILI/SILI.fs | 10 +-- VSharp.Solver/Z3.fs | 57 +++++++------- VSharp.Utils/Collections.fs | 4 +- VSharp.Utils/Stopwatch.fs | 33 ++++---- 11 files changed, 119 insertions(+), 138 deletions(-) diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 842d0a9d9..6e7d36f36 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -344,7 +344,8 @@ module API = let EmptyStack = EvaluationStack.empty module public Memory = - let EmptyState modelState = State.makeEmpty modelState + let EmptyState() = State.makeEmpty() + let EmptyStateWithModel modelState = State.makeEmptyWithModel modelState let PopFrame state = Memory.popFrame state let ForcePopFrames count state = Memory.forcePopFrames count state let PopTypeVariables state = Memory.popTypeVariablesSubstitution state @@ -469,7 +470,7 @@ module API = let InitializeStaticMembers state targetType = Memory.initializeStaticMembers state targetType - let Allocate state key term = Memory.allocateOnStack state key term + let AllocateOnStack state key term = Memory.allocateOnStack state key term let AllocateTemporaryLocalVariable state typ term = let tmpKey = TemporaryLocalVariableKey typ diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 7875289cc..6470499f3 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -210,7 +210,8 @@ module API = val EmptyStack : evaluationStack module public Memory = - val EmptyState : state option -> state + val EmptyState : unit -> state + val EmptyStateWithModel : state -> state val PopFrame : state -> unit val ForcePopFrames : int -> state -> unit val PopTypeVariables : state -> unit @@ -253,7 +254,7 @@ module API = val InitializeStaticMembers : state -> symbolicType -> unit - val Allocate : state -> stackKey -> term -> unit + val AllocateOnStack : state -> stackKey -> term -> unit val AllocateTemporaryLocalVariable : state -> System.Type -> term -> term val AllocateDefaultClass : state -> symbolicType -> term val AllocateDefaultArray : state -> term list -> symbolicType -> term diff --git a/VSharp.SILI.Core/Copying.fs b/VSharp.SILI.Core/Copying.fs index 1233f4a19..aec598922 100644 --- a/VSharp.SILI.Core/Copying.fs +++ b/VSharp.SILI.Core/Copying.fs @@ -24,8 +24,7 @@ module internal Copying = let constant = Constant "i" source Types.Int32 let leftBound = simplifyLessOrEqual lowerBound constant id let rightBound = simplifyLessOrEqual constant upperBound id - let pcWithBounds = - PC.add leftBound state.pc |> PC.add rightBound + let pcWithBounds = PC.add leftBound state.pc |> PC.add rightBound state.pc <- pcWithBounds constant diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index bbdfa13fa..faa6cbd97 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -703,7 +703,7 @@ module internal Memory = and private readStructUnsafe fields structType startByte endByte = let readField fieldId = fields.[fieldId] - readFieldsUnsafe (State.makeEmpty None) (fun _ -> __unreachable__()) readField false structType startByte endByte + readFieldsUnsafe (State.makeEmpty()) (fun _ -> __unreachable__()) readField false structType startByte endByte and private getAffectedFields state reportError readField isStatic (blockType : symbolicType) startByte endByte = let t = toDotNetType blockType @@ -1030,7 +1030,7 @@ module internal Memory = and private writeStructUnsafe structTerm fields structType startByte value = let readField fieldId = fields.[fieldId] - let updatedFields = writeFieldsUnsafe (State.makeEmpty None) (fun _ -> __unreachable__()) readField false structType startByte value + let updatedFields = writeFieldsUnsafe (State.makeEmpty()) (fun _ -> __unreachable__()) readField false structType startByte value let writeField structTerm (fieldId, value) = writeStruct structTerm fieldId value List.fold writeField structTerm updatedFields diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index f7b61ac78..af97198ad 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -26,14 +26,11 @@ module internal PC = constraints.Clear() isFalse <- true - new() = - PathCondition(HashSet(), false) + new() = PathCondition(HashSet(), false) override this.ToString() = - if (this :> IPathCondition).IsEmpty then - "true" - else - Seq.map (fun c -> $"({c})") constraints |> join " /\ " + if (this :> IPathCondition).IsEmpty then "true" + else Seq.map (fun c -> $"({c})") constraints |> join " /\ " interface IPathCondition with @@ -47,8 +44,7 @@ module internal PC = | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() | _ -> constraints.Add(newConstraint) |> ignore - member this.Copy() = - PathCondition(HashSet(constraints), isFalse) + member this.Copy() = PathCondition(HashSet(constraints), isFalse) member this.ToSeq() = seq constraints @@ -75,8 +71,7 @@ module internal PC = | Node of term | Empty - let private unwrapNode = - function + let private unwrapNode = function | Tail(constant, _) -> constant | Node(constant) -> constant | Empty -> invalidOp "Cannot unwrap empty node" @@ -110,7 +105,7 @@ module internal PC = /// let rec findPrevious constant = match nextNode constant with - | Tail _ -> Some(constant) + | Tail _ -> Some constant | Node nextTerm -> findPrevious nextTerm | Empty -> None @@ -119,9 +114,9 @@ module internal PC = /// oneConstant and anotherConstant don't need to be the representatives /// let union oneConstant anotherConstant = - match (findPrevious oneConstant), (findPrevious anotherConstant) with - | Some(onePrevious), Some(anotherPrevious) -> - match (nextNode onePrevious), (nextNode anotherPrevious) with + match findPrevious oneConstant, findPrevious anotherConstant with + | Some onePrevious, Some anotherPrevious -> + match nextNode onePrevious, nextNode anotherPrevious with | Tail(oneRepresentative, oneConstraints), Tail(anotherRepresentative, anotherConstraints) when oneRepresentative <> anotherRepresentative -> let constraintsUnion = PersistentSet.union oneConstraints anotherConstraints @@ -163,22 +158,20 @@ module internal PC = /// Constraints to add to the tail let addSubset (constants : Dictionary) constantsToAdd constraintsToAdd = let firstConstant = constantsToAdd |> Seq.head - if Seq.length constantsToAdd = 1 then - constants.[firstConstant] <- Tail(firstConstant, constraintsToAdd) - else - constantsToAdd - |> Seq.pairwise - |> Seq.iteri (fun i (previous, next) -> - if (i <> Seq.length constantsToAdd - 2) then - constants.[previous] <- Node(next) - else - constants.[previous] <- Tail(next, constraintsToAdd) - constants.[next] <- Node(firstConstant) - ) + + let addNode i (previous, next) = + if i <> Seq.length constantsToAdd - 2 then + constants.[previous] <- Node(next) + else + constants.[previous] <- Tail(next, constraintsToAdd) + constants.[next] <- Node(firstConstant) + + if Seq.length constantsToAdd = 1 then constants.[firstConstant] <- Tail(firstConstant, constraintsToAdd) + else constantsToAdd |> Seq.pairwise |> Seq.iteri addNode let addConstraintsToSubset subsetConstant constraintsToAdd = match findPrevious subsetConstant with - | Some(previous) -> + | Some previous -> match nextNode previous with | Tail(representative, constraints) -> let constraintsUnion = PersistentSet.union constraints constraintsToAdd @@ -186,24 +179,20 @@ module internal PC = | _ -> __unreachable__() | _ -> __unreachable__() - let constSourcesIndependent = - function - | ConstantT(_, oneSrc, _), ConstantT(_, anotherSrc, _) -> oneSrc.IndependentWith anotherSrc - | _ -> true + let constSourcesIndependent = function + | ConstantT(_, oneSrc, _), ConstantT(_, anotherSrc, _) -> oneSrc.IndependentWith anotherSrc + | _ -> true let addNewConstraintWithMerge newConstraint = let constraintConstants = discoverConstants [newConstraint] - let oldConstants, newConstants = - constraintConstants - |> Seq.splitBy constants.ContainsKey + let oldConstants, newConstants = constraintConstants |> Seq.splitBy constants.ContainsKey // are there constraints without constants at all? // answer: yes, in ArrayConcreteUnsafeRead, is it ok? let newConstraintSet = PersistentSet.add PersistentSet.empty newConstraint - if Seq.isEmpty newConstants |> not then - addSubset constants newConstants newConstraintSet - else if Seq.isEmpty oldConstants |> not then - addConstraintsToSubset (Seq.head oldConstants) newConstraintSet + + if Seq.isEmpty newConstants |> not then addSubset constants newConstants newConstraintSet + elif Seq.isEmpty oldConstants |> not then addConstraintsToSubset (Seq.head oldConstants) newConstraintSet constraints.Add(newConstraint) |> ignore @@ -211,27 +200,23 @@ module internal PC = |> Seq.filter (constSourcesIndependent >> not) |> Seq.iter (fun (oneConst, anotherConst) -> union oneConst anotherConst) - match (Seq.tryHead oldConstants) with - | Some(someOldConstant) -> + match Seq.tryHead oldConstants with + | Some someOldConstant -> Seq.iter (union someOldConstant) oldConstants - match (Seq.tryHead newConstants) with - | Some(someNewConstant) -> union someNewConstant someOldConstant + match Seq.tryHead newConstants with + | Some someNewConstant -> union someNewConstant someOldConstant | _ -> () | _ -> () - internal new() = - IndependentPathCondition(Dictionary(), HashSet(), false) + internal new() = IndependentPathCondition(Dictionary(), HashSet(), false) override this.ToString() = - if (this :> IPathCondition).IsEmpty then - "true" - else - Seq.map (fun c -> $"({c})") constraints |> join " /\ " + if (this :> IPathCondition).IsEmpty then "true" + else Seq.map (fun c -> $"({c})") constraints |> join " /\ " interface IPathCondition with - member this.Copy() = - IndependentPathCondition(Dictionary(constants), HashSet(constraints), isFalse) + member this.Copy() = IndependentPathCondition(Dictionary(constants), HashSet(constraints), isFalse) member this.IsFalse = isFalse @@ -264,17 +249,17 @@ module internal PC = /// one path condition are independent with constants contained in another one /// member this.Fragments = + let getSubsetByRepresentative = function + | Tail(representative, constraints) -> + let constants = Dictionary() + addSubset constants (subset representative) constraints + let constraints = HashSet(PersistentSet.toSeq constraints) + Some(IndependentPathCondition(constants, constraints, false)) + | _ -> None + if isFalse then Seq.singleton this else - let getSubsetByRepresentative = - function - | Tail(representative, constraints) -> - let constants = Dictionary() - addSubset constants (subset representative) constraints - let constraints = HashSet(PersistentSet.toSeq constraints) - Some(IndependentPathCondition(constants, constraints, false)) - | _ -> None Seq.choose getSubsetByRepresentative constants.Values |> Seq.cast @@ -291,8 +276,6 @@ module internal PC = let public unionWith anotherPc (pc : IPathCondition) = pc.UnionWith anotherPc - let public create () : IPathCondition = - if FeatureFlags.current.isConstraintIndependenceEnabled then - IndependentPathCondition() - else - PathCondition() + let public create() : IPathCondition = + if FeatureFlags.current.isConstraintIndependenceEnabled then IndependentPathCondition() + else PathCondition() diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index f0d8ef582..7d23f377b 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -36,31 +36,23 @@ module public SolverInteraction = let orderWithNull = Map.add VectorTime.zero 0 order { addressOrder = orderWithNull } + let private getOrEmpty = Option.defaultValue { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } + let private checkSatPlainly state = let ctx = getEncodingContext state let formula = state.pc.ToSeq() |> conjunction match solver with - | Some s -> - let model = - state.model - |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } - s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = model } + | Some s -> s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = getOrEmpty state.model } | None -> SmtUnknown "Solver not configured" let private checkSatIncrementally state = let ctx = getEncodingContext state let conditions = state.pc |> PC.toSeq match solver with - | Some s -> - let model = - state.model - |> Option.defaultValue { state = State.makeEmpty None; subst = Dictionary<_, _>(); complete = true } - s.CheckAssumptions ctx model conditions + | Some s -> s.CheckAssumptions ctx (getOrEmpty state.model) conditions | None -> SmtUnknown "Solver not configured" let checkSat state = // TODO: need to solve types here? #do - if FeatureFlags.current.isIncrementalityEnabled then - checkSatIncrementally state - else - checkSatPlainly state + if FeatureFlags.current.isIncrementalityEnabled then checkSatIncrementally state + else checkSatPlainly state diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index b53be6299..7d6cb70af 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -144,7 +144,8 @@ and abstract Compose : state -> term module public State = - let makeEmpty modelState = { + + let private makeEmptyInternal modelState = { id = Guid.NewGuid().ToString() pc = PC.create() evaluationStack = EvaluationStack.empty @@ -168,3 +169,7 @@ module public State = model = Option.bind (fun state -> Some {subst = Dictionary<_,_>(); state = state; complete = true}) modelState } + + let makeEmptyWithModel modelState = makeEmptyInternal (Some modelState) + + let makeEmpty() = makeEmptyInternal None diff --git a/VSharp.SILI/SILI.fs b/VSharp.SILI/SILI.fs index 9b6b71b82..6e0ed3a2a 100644 --- a/VSharp.SILI/SILI.fs +++ b/VSharp.SILI/SILI.fs @@ -15,7 +15,7 @@ type public SILI(options : SiliOptions) = let statistics = SILIStatistics() let infty = UInt32.MaxValue - let emptyState = Memory.EmptyState None + let emptyState = Memory.EmptyState() let isConcolicMode = match options.executionMode with | ConcolicMode -> true @@ -80,9 +80,9 @@ type public SILI(options : SiliOptions) = action.Invoke e static member private FormInitialStateWithoutStatics (method : MethodBase) = - let modelState = Memory.EmptyState None + let modelState = Memory.EmptyState() Memory.FillWithParametersAndThis modelState method - let initialState = Memory.EmptyState (Some modelState) + let initialState = Memory.EmptyStateWithModel modelState let cilState = makeInitialState method initialState try let this(*, isMethodOfStruct*) = @@ -199,9 +199,9 @@ type public SILI(options : SiliOptions) = reportIncomplete <- wrapOnIIE onIIE reportInternalFail <- wrapOnInternalFail onInternalFail interpreter.ConfigureErrorReporter reportError - let modelState = Memory.EmptyState None + let modelState = Memory.EmptyState() Memory.FillWithParametersAndThis modelState method - let state = Memory.EmptyState (Some modelState) + let state = Memory.EmptyStateWithModel modelState let argsToState args = let argTerms = Seq.map (fun str -> Memory.AllocateString str state) args let stringType = Types.FromDotNetType typeof diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 941d61c6f..abccb140b 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -37,30 +37,30 @@ module internal Z3 = type private encodingCache = { sorts : IDictionary - e2t : IDictionary - t2e : IDictionary + expressionToTerm : IDictionary + termToExpression : IDictionary heapAddresses : IDictionary staticKeys : IDictionary regionConstants : Dictionary mutable lastSymbolicAddress : int32 } with member x.Get(term, encoder : unit -> Expr) = - Dict.tryGetValue2 x.t2e term (fun () -> + Dict.tryGetValue2 x.termToExpression term (fun () -> let result = {expr = encoder(); assumptions = List.empty} - x.e2t.[result.expr] <- term - x.t2e.[term] <- result + x.expressionToTerm.[result.expr] <- term + x.termToExpression.[term] <- result result) member x.Get(term, encoder : unit -> encodingResult) = - Dict.tryGetValue2 x.t2e term (fun () -> + Dict.tryGetValue2 x.termToExpression term (fun () -> let result = encoder() - x.e2t.[result.expr] <- term - x.t2e.[term] <- result + x.expressionToTerm.[result.expr] <- term + x.termToExpression.[term] <- result result) let private freshCache () = { sorts = Dictionary() - e2t = Dictionary() - t2e = Dictionary() + expressionToTerm = Dictionary() + termToExpression = Dictionary() heapAddresses = Dictionary() staticKeys = Dictionary() regionConstants = Dictionary() @@ -71,7 +71,7 @@ module internal Z3 = type private Z3Builder(ctx : Context) = let mutable encodingCache = freshCache() - let emptyState = Memory.EmptyState None + let emptyState = Memory.EmptyState() let getMemoryConstant mkConst (typ : regionSort * fieldId list) = let result : ArrayExpr ref = ref null @@ -84,8 +84,8 @@ module internal Z3 = member x.Reset() = encodingCache <- freshCache() - member x.ClearT2E() = - encodingCache.t2e.Clear() + member x.ClearTermToExpressionCache() = + encodingCache.termToExpression.Clear() member private x.ValidateId id = assert(not <| String.IsNullOrWhiteSpace id) @@ -617,12 +617,12 @@ module internal Z3 = let address = x.DecodeConcreteHeapAddress t bv |> ConcreteHeapAddress HeapRef address t | :? BitVecExpr as bv when bv.IsConst -> - if encodingCache.e2t.ContainsKey(expr) then encodingCache.e2t.[expr] + if encodingCache.expressionToTerm.ContainsKey(expr) then encodingCache.expressionToTerm.[expr] else x.GetTypeOfBV bv |> Concrete expr.String | :? IntNum as i -> Concrete i.Int (Numeric (Id typeof)) | :? RatNum as r -> Concrete (double(r.Numerator.Int) * 1.0 / double(r.Denominator.Int)) (Numeric (Id typeof)) | _ -> - if encodingCache.e2t.ContainsKey(expr) then encodingCache.e2t.[expr] + if encodingCache.expressionToTerm.ContainsKey(expr) then encodingCache.expressionToTerm.[expr] elif expr.IsTrue then True elif expr.IsFalse then False elif expr.IsNot then x.DecodeBoolExpr OperationType.LogicalNot expr @@ -661,7 +661,7 @@ module internal Z3 = member x.UpdateModel (z3Model : Model) (targetModel : model) = let stackEntries = Dictionary() - encodingCache.t2e |> Seq.iter (fun kvp -> + encodingCache.termToExpression |> Seq.iter (fun kvp -> match kvp.Key with | {term = Constant(_, StructFieldChain(fields, StackReading(key)), t)} -> let refinedExpr = z3Model.Eval(kvp.Value.expr, false) @@ -682,13 +682,13 @@ module internal Z3 = targetModel.subst.[source] <- term | _ -> ()) - if (Memory.IsStackEmpty targetModel.state) then + if Memory.IsStackEmpty targetModel.state then Memory.NewStackFrame targetModel.state null List.empty stackEntries |> Seq.iter (fun kvp -> let key = kvp.Key let term = !kvp.Value - Memory.Allocate targetModel.state key term) + Memory.AllocateOnStack targetModel.state key term) let defaultValues = Dictionary() encodingCache.regionConstants |> Seq.iter (fun kvp -> @@ -736,7 +736,7 @@ module internal Z3 = targetModel.state.startingTime <- VectorTime.min targetModel.state.startingTime [encodingCache.lastSymbolicAddress - 1] encodingCache.heapAddresses.Clear() - encodingCache.t2e.Clear() + encodingCache.termToExpression.Clear() let private ctx = new Context() let private builder = Z3Builder(ctx) @@ -812,13 +812,13 @@ module internal Z3 = // pathAtoms // |> Seq.filter (fun atom -> z3Model.Eval(atom, false).IsTrue) // |> Seq.map (fun atom -> paths.[atom]) - builder.ClearT2E() + builder.ClearTermToExpressionCache() SmtSat { mdl = updatedModel; usedPaths = [](*usedPaths*) } | Status.UNSATISFIABLE -> - builder.ClearT2E() + builder.ClearTermToExpressionCache() SmtUnsat { core = Array.empty (*optCtx.UnsatCore |> Array.map (builder.Decode Bool)*) } | Status.UNKNOWN -> - builder.ClearT2E() + builder.ClearTermToExpressionCache() SmtUnknown optCtx.ReasonUnknown | _ -> __unreachable__() with @@ -853,13 +853,13 @@ module internal Z3 = seq { yield! Seq.cast encoded.assumptions yield encoded.expr :?> BoolExpr - } + } try let exprs = Seq.collect encodeToBoolExprs formulas let boolConsts = seq { for expr in exprs do let mutable name = "" - if (assumptions.TryGetValue(expr, &name)) then + if assumptions.TryGetValue(expr, &name) then yield ctx.MkBoolConst name else name <- $"p{assumptions.Count}" @@ -871,21 +871,20 @@ module internal Z3 = yield boolConst } let names = boolConsts |> Seq.map (fun c -> c.ToString()) - let amp = " & " - printLog Trace $"SOLVER: check: {join amp names}" + printLog Trace $"""SOLVER: check: {join " & " names}""" let result = optCtx.Check boolConsts match result with | Status.SATISFIABLE -> let z3Model = optCtx.Model let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} builder.UpdateModel z3Model updatedModel - builder.ClearT2E() + builder.ClearTermToExpressionCache() SmtSat { mdl = updatedModel; usedPaths = [] } | Status.UNSATISFIABLE -> - builder.ClearT2E() + builder.ClearTermToExpressionCache() SmtUnsat { core = Array.empty } | Status.UNKNOWN -> - builder.ClearT2E() + builder.ClearTermToExpressionCache() SmtUnknown optCtx.ReasonUnknown | _ -> __unreachable__() with diff --git a/VSharp.Utils/Collections.fs b/VSharp.Utils/Collections.fs index 707558890..be5b7bd69 100644 --- a/VSharp.Utils/Collections.fs +++ b/VSharp.Utils/Collections.fs @@ -37,8 +37,8 @@ module public Seq = /// let splitBy condition seq = let grouped = Seq.groupBy (fun element -> condition element) seq - match (Seq.tryFind (fun (value, _) -> value) grouped), - (Seq.tryFind (fun (value, _) -> value |> not) grouped) with + match Seq.tryFind (fun (value, _) -> value) grouped, + Seq.tryFind (fun (value, _) -> value |> not) grouped with | Some(_, trueSeq), Some(_, falseSeq) -> trueSeq, falseSeq | Some(_, trueSeq), None -> trueSeq, Seq.empty | None, Some(_, falseSeq) -> Seq.empty, falseSeq diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs index 45146f7ea..92262793b 100644 --- a/VSharp.Utils/Stopwatch.fs +++ b/VSharp.Utils/Stopwatch.fs @@ -30,7 +30,7 @@ module Stopwatch = let private csvFilename = "benchmark.csv" let private measurements = Dictionary() - let private getGitCommitHash () = + let private getGitCommitHash() = let procStartInfo = ProcessStartInfo("git", "rev-parse --short HEAD") procStartInfo.RedirectStandardOutput <- true @@ -52,7 +52,7 @@ module Stopwatch = /// Function to run let public runMeasuringTime tag action = let measurement = - if (measurements.ContainsKey tag) then + if measurements.ContainsKey tag then measurements.[tag] else let newMeasurement = { stopwatch = Stopwatch(); timesCalled = 0 } @@ -72,7 +72,7 @@ module Stopwatch = /// /// Stops all running measurements /// - let public stopAll () = + let public stopAll() = measurements |> Seq.map (|KeyValue|) |> Seq.iter (fun (_, m) -> m.stopwatch.Stop()) @@ -88,21 +88,22 @@ module Stopwatch = let currentDateTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") let commitHash = getGitCommitHash() + let createRecord (tag, measurement : measurement) = + { + commitHash = commitHash + dateTime = currentDateTime + caseName = caseName + tag = tag + timesCalled = measurement.timesCalled + totalTicks = measurement.stopwatch.ElapsedTicks + totalMs = measurement.stopwatch.ElapsedMilliseconds + testsGenerated = testsGenerated + } + let records = measurements |> Seq.map (|KeyValue|) - |> Seq.map (fun (tag, m) -> - { - commitHash = commitHash - dateTime = currentDateTime - caseName = caseName - tag = tag - timesCalled = m.timesCalled - totalTicks = m.stopwatch.ElapsedTicks - totalMs = m.stopwatch.ElapsedMilliseconds - testsGenerated = testsGenerated - } - ) + |> Seq.map createRecord let targetPath = Path.Combine(csvPath, csvFilename) @@ -119,4 +120,4 @@ module Stopwatch = /// /// Clears all current measurements /// - let public clear () = measurements.Clear() + let public clear() = measurements.Clear() From cef420d0c81294aee249ee2dadd5788123f35cb5 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Tue, 19 Jul 2022 22:50:26 +0300 Subject: [PATCH 77/88] [fix] Fix runtime submodule version --- runtime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime b/runtime index d7619cd4b..3a25a7f1c 160000 --- a/runtime +++ b/runtime @@ -1 +1 @@ -Subproject commit d7619cd4b165c13430484e381042f055d4c5a9c7 +Subproject commit 3a25a7f1cc446b60678ed25c9d829420d6321eba From de680e36d68c1b509f5659c0046623e12b4195dc Mon Sep 17 00:00:00 2001 From: mxprshn Date: Fri, 22 Jul 2022 02:26:40 +0300 Subject: [PATCH 78/88] [fix] Add API level options class and remove eval condition option --- VSharp.API/Options.cs | 84 +++++++++++++++++++++++++++ VSharp.API/SvmOptions.cs | 24 -------- VSharp.API/VSharp.cs | 72 ++++++++++------------- VSharp.Runner/RunnerProgram.cs | 73 +++++++++++++++-------- VSharp.SILI.Core/API.fs | 9 ++- VSharp.SILI.Core/API.fsi | 5 +- VSharp.SILI.Core/Branching.fs | 7 +-- VSharp.SILI.Core/FeatureFlags.fs | 20 ++----- VSharp.SILI.Core/PathCondition.fs | 2 +- VSharp.SILI.Core/SolverInteraction.fs | 13 +++-- VSharp.SILI.Core/State.fs | 44 +++++++------- VSharp.SILI/Options.fs | 8 ++- VSharp.SILI/SILI.fs | 2 +- VSharp.Test/IntegrationTests.cs | 10 ++-- 14 files changed, 222 insertions(+), 151 deletions(-) create mode 100644 VSharp.API/Options.cs delete mode 100644 VSharp.API/SvmOptions.cs diff --git a/VSharp.API/Options.cs b/VSharp.API/Options.cs new file mode 100644 index 000000000..a9fc20752 --- /dev/null +++ b/VSharp.API/Options.cs @@ -0,0 +1,84 @@ +using VSharp.Interpreter.IL; + +namespace VSharp +{ + /// + /// + /// + public enum ExecutionMode + { + Symbolic, + Concolic + } + + /// + /// + /// + public enum SearchMode + { + DFS, + BFS, + Guided + } + + /// + /// + /// + public enum CoverageZone + { + Method, + Class, + Module + } + + /// + /// + /// + /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// + /// + /// + /// + /// + /// + public readonly record struct CoverOptions( + string OutputDirectory = "", + SearchMode SearchMode = SearchMode.Guided, + CoverageZone CoverageZone = CoverageZone.Method, + ExecutionMode ExecutionMode = ExecutionMode.Symbolic, + uint RecThreshold = 0u, + bool IsConstraintIndependenceEnabled = true, + bool IsSolverIncrementalityEnabled = false + ) + { + internal siliOptions toSiliOptions() + { + var coverageZone = CoverageZone switch + { + CoverageZone.Method => Interpreter.IL.coverageZone.MethodZone, + CoverageZone.Class => Interpreter.IL.coverageZone.ClassZone, + CoverageZone.Module => Interpreter.IL.coverageZone.ModuleZone + }; + + var searchMode = SearchMode switch + { + SearchMode.DFS => Interpreter.IL.searchMode.DFSMode, + SearchMode.BFS => Interpreter.IL.searchMode.BFSMode, + SearchMode.Guided => Interpreter.IL.searchMode.GuidedMode + }; + + var executionMode = ExecutionMode switch + { + ExecutionMode.Symbolic => Interpreter.IL.executionMode.SymbolicMode, + ExecutionMode.Concolic => Interpreter.IL.executionMode.ConcolicMode + }; + + return new siliOptions( + OutputDirectory, + explorationMode.NewTestCoverageMode(coverageZone, searchMode), + executionMode, + RecThreshold + ); + } + } +} diff --git a/VSharp.API/SvmOptions.cs b/VSharp.API/SvmOptions.cs deleted file mode 100644 index 2625b035e..000000000 --- a/VSharp.API/SvmOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using VSharp.Core; - -namespace VSharp -{ - /// - /// Advanced symbolic virtual machine options. - /// - /// If true, independent constraint sets are maintained (constraint independence optimization). - /// If true, branch condition is evaluated with current model to avoid extra SMT solver queries. - /// If true, SMT solver works in incremental mode. - public readonly record struct SvmOptions( - bool IsConstraintIndependenceEnabled = false, - bool IsConditionEvaluationEnabled = false, - bool IsSolverIncrementalityEnabled = false - ) - { - internal featureFlags GetFeatureFlags() => - new featureFlags( - isConstraintIndependenceEnabled: IsConstraintIndependenceEnabled, - isConditionEvaluationEnabled: IsConditionEvaluationEnabled, - isIncrementalityEnabled: IsSolverIncrementalityEnabled - ); - } -} diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index 62df8a4e4..304bcdc25 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -71,16 +71,12 @@ public void GenerateReport(TextWriter writer) public static class TestGenerator { - private static Statistics StartExploration(List methods, string resultsFolder, SvmOptions svmOptions, string[] mainArguments = null) + private static Statistics StartExploration(List methods, CoverOptions options, string[] mainArguments = null) { - var recThreshold = 0u; - var options = - new SiliOptions(explorationMode.NewTestCoverageMode(coverageZone.MethodZone, searchMode.GuidedMode), - executionMode.SymbolicMode, recThreshold); - SILI explorer = new SILI(options); - UnitTests unitTests = new UnitTests(resultsFolder); - Core.API.ConfigureSolver(SolverPool.mkSolver()); - Core.API.SetFeatureFlags(svmOptions.GetFeatureFlags()); + SILI explorer = new SILI(options.toSiliOptions()); + UnitTests unitTests = new UnitTests(options.OutputDirectory); + Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); + Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled); // TODO: single solver instance for multiple methods causes problems in incrementality mode foreach (var method in methods) @@ -113,24 +109,22 @@ private static bool Reproduce(DirectoryInfo testDir) /// Generates test coverage for specified method. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// Summary of tests generation process. - public static Statistics Cover(MethodBase method, string outputDirectory = "", SvmOptions svmOptions = new()) + public static Statistics Cover(MethodBase method, CoverOptions options = new()) { List methods = new List {method}; - return StartExploration(methods, outputDirectory, svmOptions); + return StartExploration(methods, options); } /// /// Generates test coverage for all public methods of specified type. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// Summary of tests generation process. /// Thrown if specified class does not contain public methods. - public static Statistics Cover(Type type, string outputDirectory = "", SvmOptions svmOptions = new()) + public static Statistics Cover(Type type, CoverOptions options = new()) { List methods = new List(type.GetConstructors()); @@ -143,19 +137,18 @@ private static bool Reproduce(DirectoryInfo testDir) throw new ArgumentException("I've not found any public method or constructor of class " + type.FullName); } - return StartExploration(methods, outputDirectory, svmOptions); + return StartExploration(methods, options); } /// /// Generates test coverage for all public methods of all public classes in the specified assembly. /// /// Assembly to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// Summary of tests generation process. /// Thrown if no public methods found in assembly. /// - public static Statistics Cover(Assembly assembly, string outputDirectory = "", SvmOptions svmOptions = new()) + public static Statistics Cover(Assembly assembly, CoverOptions options = new()) { List methods; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | @@ -177,7 +170,7 @@ private static bool Reproduce(DirectoryInfo testDir) throw new ArgumentException("I've not found any public method in assembly"); } - return StartExploration(methods, outputDirectory, svmOptions); + return StartExploration(methods, options); } /// @@ -185,12 +178,11 @@ private static bool Reproduce(DirectoryInfo testDir) /// /// Assembly to be covered with tests. /// Command line arguments of entry point - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// Summary of tests generation process. /// Thrown if assembly does not contain entry point. /// - public static Statistics Cover(Assembly assembly, string[] args, string outputDirectory = "", SvmOptions svmOptions = new()) + public static Statistics Cover(Assembly assembly, string[] args, CoverOptions options = new()) { List methods; var entryPoint = assembly.EntryPoint; @@ -200,19 +192,18 @@ private static bool Reproduce(DirectoryInfo testDir) } methods = new List { entryPoint }; - return StartExploration(methods, outputDirectory, svmOptions, args); + return StartExploration(methods, options, args); } /// /// Generates test coverage for the specified method and runs all tests. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// True if all generated tests have passed. - public static bool CoverAndRun(MethodBase method, string outputDirectory = "", SvmOptions svmOptions = new()) + public static bool CoverAndRun(MethodBase method, CoverOptions options = new()) { - var stats = Cover(method, outputDirectory, svmOptions); + var stats = Cover(method, options); return Reproduce(stats.OutputDir); } @@ -220,13 +211,12 @@ private static bool Reproduce(DirectoryInfo testDir) /// Generates test coverage for the specified type and runs all tests. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// True if all generated tests have passed. /// Thrown if specified class does not contain public methods. - public static bool CoverAndRun(Type type, string outputDirectory = "", SvmOptions svmOptions = new()) + public static bool CoverAndRun(Type type, CoverOptions options = new()) { - var stats = Cover(type, outputDirectory, svmOptions); + var stats = Cover(type, options); return Reproduce(stats.OutputDir); } @@ -234,14 +224,13 @@ private static bool Reproduce(DirectoryInfo testDir) /// Generates test coverage for all public methods of all public classes of the specified assembly and runs all tests. /// /// Assembly to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Additional parameters of the run. /// True if all generated tests have passed. /// Thrown if no public methods found in assembly. /// - public static bool CoverAndRun(Assembly assembly, string outputDirectory = "", SvmOptions svmOptions = new()) + public static bool CoverAndRun(Assembly assembly, CoverOptions options = new()) { - var stats = Cover(assembly, outputDirectory, svmOptions); + var stats = Cover(assembly, options); return Reproduce(stats.OutputDir); } @@ -249,14 +238,13 @@ private static bool Reproduce(DirectoryInfo testDir) /// Generates test coverage for entry point of the specified assembly and runs all tests. /// /// Assembly to be covered with tests. - /// Command line arguments of entry point - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// Advanced symbolic virtual machine options. + /// Command line arguments of entry point. + /// Additional parameters of the run. /// True if all generated tests have passed. /// Thrown if assembly does not contain entry point. - public static bool CoverAndRun(Assembly assembly, string[] args, string outputDirectory = "", SvmOptions svmOptions = new()) + public static bool CoverAndRun(Assembly assembly, string[] args, CoverOptions options = new()) { - var stats = Cover(assembly, args, outputDirectory, svmOptions); + var stats = Cover(assembly, args, options); return Reproduce(stats.OutputDir); } diff --git a/VSharp.Runner/RunnerProgram.cs b/VSharp.Runner/RunnerProgram.cs index 8bc6ac94f..5a9a83a67 100644 --- a/VSharp.Runner/RunnerProgram.cs +++ b/VSharp.Runner/RunnerProgram.cs @@ -121,9 +121,16 @@ public static int Main(string[] args) var unknownArgsOption = new Option("--unknown-args", description: "Force engine to generate various input console arguments"); - var constraintIndependenceOption = new Option("--c-independence", description: "Advanced: maintain independent constraint sets (constraint independence optimization)"); - var conditionEvaluationOption = new Option("--eval-condition", description: "Advanced: evaluate branch condition with current model to avoid extra SMT solver queries"); - var incrementalityOption = new Option("--incrementality", description: "Advanced: enable SMT solver incremental mode"); + var constraintIndependenceOption = new Option( + "--c-independence", + description: "Maintain independent constraint sets (constraint independence optimization). True by default", + getDefaultValue: () => true + ); + var incrementalityOption = new Option( + "--incrementality", + description: "Enable SMT solver incremental mode. False by default", + getDefaultValue: () => false + ); var rootCommand = new RootCommand(); @@ -135,7 +142,6 @@ public static int Main(string[] args) entryPointCommand.AddGlobalOption(outputOption); entryPointCommand.AddOption(unknownArgsOption); entryPointCommand.AddOption(constraintIndependenceOption); - entryPointCommand.AddOption(conditionEvaluationOption); entryPointCommand.AddOption(incrementalityOption); var allPublicMethodsCommand = @@ -144,7 +150,6 @@ public static int Main(string[] args) allPublicMethodsCommand.AddArgument(assemblyPathArgument); allPublicMethodsCommand.AddGlobalOption(outputOption); allPublicMethodsCommand.AddOption(constraintIndependenceOption); - allPublicMethodsCommand.AddOption(conditionEvaluationOption); allPublicMethodsCommand.AddOption(incrementalityOption); var publicMethodsOfClassCommand = @@ -155,7 +160,6 @@ public static int Main(string[] args) publicMethodsOfClassCommand.AddArgument(assemblyPathArgument); publicMethodsOfClassCommand.AddGlobalOption(outputOption); publicMethodsOfClassCommand.AddOption(constraintIndependenceOption); - publicMethodsOfClassCommand.AddOption(conditionEvaluationOption); publicMethodsOfClassCommand.AddOption(incrementalityOption); var specificMethodCommand = @@ -166,61 +170,84 @@ public static int Main(string[] args) specificMethodCommand.AddArgument(assemblyPathArgument); specificMethodCommand.AddGlobalOption(outputOption); specificMethodCommand.AddOption(constraintIndependenceOption); - specificMethodCommand.AddOption(conditionEvaluationOption); specificMethodCommand.AddOption(incrementalityOption); rootCommand.Description = "Symbolic execution engine for .NET"; - entryPointCommand.Handler = CommandHandler.Create + entryPointCommand.Handler = CommandHandler.Create ( - (assemblyPath, args, output, unknownArgs, cIndependence, evalCondition, incrementality) => + (assemblyPath, args, output, unknownArgs, cIndependence, incrementality) => { - var options = new SvmOptions(cIndependence, evalCondition, incrementality); var assembly = ResolveAssembly(assemblyPath); if (unknownArgs) args = null; if (assembly != null) - PostProcess(TestGenerator.Cover(assembly, args, output.FullName, options)); + { + var options = new CoverOptions( + OutputDirectory: output.FullName, + IsConstraintIndependenceEnabled: cIndependence, + IsSolverIncrementalityEnabled: incrementality + ); + + PostProcess(TestGenerator.Cover(assembly, args, options)); + } } ); - allPublicMethodsCommand.Handler = CommandHandler.Create + allPublicMethodsCommand.Handler = CommandHandler.Create ( - (assemblyPath, output, cIndependence, evalCondition, incrementality) => + (assemblyPath, output, cIndependence, incrementality) => { - var options = new SvmOptions(cIndependence, evalCondition, incrementality); var assembly = ResolveAssembly(assemblyPath); if (assembly != null) - PostProcess(TestGenerator.Cover(assembly, output.FullName, options)); + { + var options = new CoverOptions( + OutputDirectory: output.FullName, + IsConstraintIndependenceEnabled: cIndependence, + IsSolverIncrementalityEnabled: incrementality + ); + + PostProcess(TestGenerator.Cover(assembly, options)); + } } ); - publicMethodsOfClassCommand.Handler = CommandHandler.Create + publicMethodsOfClassCommand.Handler = CommandHandler.Create ( - (className, assemblyPath, output, cIndependence, evalCondition, incrementality) => + (className, assemblyPath, output, cIndependence, incrementality) => { - var options = new SvmOptions(cIndependence, evalCondition, incrementality); var assembly = ResolveAssembly(assemblyPath); if (assembly != null) { var type = ResolveType(assembly, className); if (type != null) { - PostProcess(TestGenerator.Cover(type, output.FullName, options)); + var options = new CoverOptions( + OutputDirectory: output.FullName, + IsConstraintIndependenceEnabled: cIndependence, + IsSolverIncrementalityEnabled: incrementality + ); + + PostProcess(TestGenerator.Cover(type, options)); } } } ); - specificMethodCommand.Handler = CommandHandler.Create + specificMethodCommand.Handler = CommandHandler.Create ( - (methodName, assemblyPath, output, cIndependence, evalCondition, incrementality) => + (methodName, assemblyPath, output, cIndependence, incrementality) => { - var options = new SvmOptions(cIndependence, evalCondition, incrementality); var assembly = ResolveAssembly(assemblyPath); if (assembly != null) { var method = ResolveMethod(assembly, methodName); if (method != null) { - PostProcess(TestGenerator.Cover(method, output.FullName, options)); + var options = new CoverOptions( + OutputDirectory: output.FullName, + IsConstraintIndependenceEnabled: cIndependence, + IsSolverIncrementalityEnabled: incrementality + ); + + PostProcess(TestGenerator.Cover(method, options)); } } } diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index 6e7d36f36..d3cfa91c4 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -6,11 +6,10 @@ open FSharpx.Collections open VSharp module API = - let SetFeatureFlags flags = - FeatureFlags.set flags - - let ConfigureSolver solver = - SolverInteraction.configureSolver solver + let SetConstraintIndependenceEnabled enabled = + FeatureFlags.setConstraintIndependenceEnabled enabled + let ConfigureSolver solver enableIncrementalMode = + SolverInteraction.configureSolver solver enableIncrementalMode let ConfigureSimplifier simplifier = configureSimplifier simplifier diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 6470499f3..211fe734c 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -5,9 +5,8 @@ open System.Reflection [] module API = - val SetFeatureFlags : featureFlags -> unit - - val ConfigureSolver : SolverInteraction.ISolver -> unit + val SetConstraintIndependenceEnabled : bool -> unit + val ConfigureSolver : SolverInteraction.ISolver -> bool -> unit val ConfigureSimplifier : IPropositionalSimplifier -> unit val Reset : unit -> unit val SaveConfiguration : unit -> unit diff --git a/VSharp.SILI.Core/Branching.fs b/VSharp.SILI.Core/Branching.fs index 9e6a84c04..d4105510c 100644 --- a/VSharp.SILI.Core/Branching.fs +++ b/VSharp.SILI.Core/Branching.fs @@ -181,8 +181,7 @@ module internal Branching = checkBothBranches()) let branch copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let isEvalEnabled = FeatureFlags.current.isConditionEvaluationEnabled - if FeatureFlags.current.isConstraintIndependenceEnabled then - executionWithConstraintIndependence isEvalEnabled copy state conditionInvocation thenBranch elseBranch merge2Results k + if FeatureFlags.isConstraintIndependenceEnabled() then + executionWithConstraintIndependence true copy state conditionInvocation thenBranch elseBranch merge2Results k else - executionWithoutConstraintIndependence isEvalEnabled copy state conditionInvocation thenBranch elseBranch merge2Results k + executionWithoutConstraintIndependence true copy state conditionInvocation thenBranch elseBranch merge2Results k diff --git a/VSharp.SILI.Core/FeatureFlags.fs b/VSharp.SILI.Core/FeatureFlags.fs index 582b2df53..8f8327645 100644 --- a/VSharp.SILI.Core/FeatureFlags.fs +++ b/VSharp.SILI.Core/FeatureFlags.fs @@ -1,18 +1,10 @@ namespace VSharp.Core -type public featureFlags = { - isConstraintIndependenceEnabled : bool - isConditionEvaluationEnabled : bool - isIncrementalityEnabled : bool -} - module internal FeatureFlags = - - let mutable current = { - isConstraintIndependenceEnabled = false - isConditionEvaluationEnabled = false - isIncrementalityEnabled = false - } - let public set flags = - current <- flags + let mutable private _isConstraintIndependenceEnabled = false + + let public isConstraintIndependenceEnabled() = _isConstraintIndependenceEnabled + + let public setConstraintIndependenceEnabled enabled = + _isConstraintIndependenceEnabled <- enabled diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index af97198ad..4a1e1c312 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -277,5 +277,5 @@ module internal PC = let public unionWith anotherPc (pc : IPathCondition) = pc.UnionWith anotherPc let public create() : IPathCondition = - if FeatureFlags.current.isConstraintIndependenceEnabled then IndependentPathCondition() + if FeatureFlags.isConstraintIndependenceEnabled() then IndependentPathCondition() else PathCondition() diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 7d23f377b..f3eaec04b 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -25,9 +25,12 @@ module public SolverInteraction = abstract AddPath : encodingContext -> path -> unit abstract CheckAssumptions : encodingContext -> model -> formula seq -> smtResult - let mutable private solver : ISolver option = None + let mutable private mSolver : ISolver option = None + let mutable private isIncrementalModeEnabled : bool = false - let configureSolver s = solver <- Some s + let configureSolver solver enableIncrementalMode = + mSolver <- Some solver + isIncrementalModeEnabled <- enableIncrementalMode let getEncodingContext (state : state) = let addresses = PersistentDict.keys state.allocatedTypes @@ -41,18 +44,18 @@ module public SolverInteraction = let private checkSatPlainly state = let ctx = getEncodingContext state let formula = state.pc.ToSeq() |> conjunction - match solver with + match mSolver with | Some s -> s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = getOrEmpty state.model } | None -> SmtUnknown "Solver not configured" let private checkSatIncrementally state = let ctx = getEncodingContext state let conditions = state.pc |> PC.toSeq - match solver with + match mSolver with | Some s -> s.CheckAssumptions ctx (getOrEmpty state.model) conditions | None -> SmtUnknown "Solver not configured" let checkSat state = // TODO: need to solve types here? #do - if FeatureFlags.current.isIncrementalityEnabled then checkSatIncrementally state + if isIncrementalModeEnabled then checkSatIncrementally state else checkSatPlainly state diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 7d6cb70af..1e20e629d 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -115,28 +115,28 @@ with and [] state = { - id : string - mutable pc : IPathCondition - mutable evaluationStack : evaluationStack - mutable stack : callStack // Arguments and local variables - mutable stackBuffers : pdict // Buffers allocated via stackAlloc - mutable classFields : pdict // Fields of classes in heap - mutable arrays : pdict // Contents of arrays in heap - mutable lengths : pdict // Lengths by dimensions of arrays in heap - mutable lowerBounds : pdict // Lower bounds by dimensions of arrays in heap - mutable staticFields : pdict // Static fields of types without type variables - mutable boxedLocations : pdict // Value types boxed in heap - mutable initializedTypes : symbolicTypeSet // Types with initialized static members - concreteMemory : concreteMemory // Fully concrete objects - mutable physToVirt : pdict // Map from physical address (obj) to concreteHeapAddress - mutable allocatedTypes : pdict // Types of heap locations allocated via new - mutable typeVariables : typeVariables // Type variables assignment in the current state - mutable delegates : pdict // Subtypes of System.Delegate allocated in heap - mutable currentTime : vectorTime // Current timestamp (and next allocated address as well) in this state - mutable startingTime : vectorTime // Timestamp before which all allocated addresses will be considered symbolic - mutable exceptionsRegister : exceptionRegister // Heap-address of exception object - mutable model : model option -} + id : string + mutable pc : IPathCondition + mutable evaluationStack : evaluationStack + mutable stack : callStack // Arguments and local variables + mutable stackBuffers : pdict // Buffers allocated via stackAlloc + mutable classFields : pdict // Fields of classes in heap + mutable arrays : pdict // Contents of arrays in heap + mutable lengths : pdict // Lengths by dimensions of arrays in heap + mutable lowerBounds : pdict // Lower bounds by dimensions of arrays in heap + mutable staticFields : pdict // Static fields of types without type variables + mutable boxedLocations : pdict // Value types boxed in heap + mutable initializedTypes : symbolicTypeSet // Types with initialized static members + concreteMemory : concreteMemory // Fully concrete objects + mutable physToVirt : pdict // Map from physical address (obj) to concreteHeapAddress + mutable allocatedTypes : pdict // Types of heap locations allocated via new + mutable typeVariables : typeVariables // Type variables assignment in the current state + mutable delegates : pdict // Subtypes of System.Delegate allocated in heap + mutable currentTime : vectorTime // Current timestamp (and next allocated address as well) in this state + mutable startingTime : vectorTime // Timestamp before which all allocated addresses will be considered symbolic + mutable exceptionsRegister : exceptionRegister // Heap-address of exception object + mutable model : model option + } and IStatedSymbolicConstantSource = diff --git a/VSharp.SILI/Options.fs b/VSharp.SILI/Options.fs index b3ea1ce30..24c4934b6 100644 --- a/VSharp.SILI/Options.fs +++ b/VSharp.SILI/Options.fs @@ -20,5 +20,9 @@ type executionMode = | ConcolicMode | SymbolicMode -type SiliOptions = - {explorationMode : explorationMode; executionMode : executionMode; recThreshold : uint32} +type siliOptions = { + outputDirectory : string + explorationMode : explorationMode + executionMode : executionMode + recThreshold : uint32 +} diff --git a/VSharp.SILI/SILI.fs b/VSharp.SILI/SILI.fs index 6e0ed3a2a..c2a0f2cb8 100644 --- a/VSharp.SILI/SILI.fs +++ b/VSharp.SILI/SILI.fs @@ -11,7 +11,7 @@ open VSharp.Core open CilStateOperations open VSharp.Solver -type public SILI(options : SiliOptions) = +type public SILI(options : siliOptions) = let statistics = SILIStatistics() let infty = UInt32.MaxValue diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 37f108271..d01a9ddcd 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -10,7 +10,6 @@ using NUnit.Framework.Internal; using NUnit.Framework.Internal.Builders; using NUnit.Framework.Internal.Commands; -using VSharp.Core; using VSharp.Interpreter.IL; using VSharp.Solver; @@ -27,7 +26,7 @@ public IEnumerable BuildFrom(ITypeInfo typeInfo) public class TestSvmAttribute : NUnitAttribute, IWrapTestMethod, ISimpleTestBuilder { - private static SiliOptions _options = null; + private static siliOptions _options = null; static TestSvmAttribute() { @@ -95,14 +94,15 @@ public TestSvmCommand( private TestResult Explore(TestExecutionContext context) { - Core.API.ConfigureSolver(SolverPool.mkSolver()); - Core.API.SetFeatureFlags(new featureFlags(false, false, false)); + Core.API.ConfigureSolver(SolverPool.mkSolver(), false); + Core.API.SetConstraintIndependenceEnabled(true); var methodInfo = innerCommand.Test.Method.MethodInfo; try { - _options = new SiliOptions + _options = new siliOptions ( + "", explorationMode.NewTestCoverageMode(coverageZone.MethodZone, searchMode.GuidedMode), _executionMode, _recThresholdForTest From a8226e17254caab5cab045b9c4d1b51c9ff2ccf3 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Fri, 22 Jul 2022 14:48:39 +0300 Subject: [PATCH 79/88] [fix] Fix conflicts after master merge --- VSharp.API/Options.cs | 34 +++++++---- VSharp.API/VSharp.cs | 40 ++++++------- VSharp.Runner/RunnerProgram.cs | 30 +++++----- VSharp.SILI.Core/API.fs | 6 +- VSharp.SILI.Core/API.fsi | 1 - VSharp.SILI.Core/Branching.fs | 30 +++++++--- VSharp.SILI.Core/Copying.fs | 3 +- VSharp.SILI.Core/Memory.fs | 25 ++------- VSharp.SILI.Core/Merging.fs | 2 +- VSharp.SILI.Core/PathCondition.fs | 2 +- VSharp.SILI.Core/SolverInteraction.fs | 10 ++-- VSharp.SILI.Core/State.fs | 56 +++++++++---------- VSharp.SILI.Core/VSharp.SILI.Core.fsproj | 1 - VSharp.SILI/SILI.fs | 4 +- VSharp.Solver/Z3.fs | 12 ++-- VSharp.Test/IntegrationTests.cs | 9 +-- .../Tests/LoanExam/CreditCalculator.cs | 3 +- VSharp.Test/VSharp.Test.csproj | 1 + 18 files changed, 135 insertions(+), 134 deletions(-) diff --git a/VSharp.API/Options.cs b/VSharp.API/Options.cs index a9fc20752..0fc767958 100644 --- a/VSharp.API/Options.cs +++ b/VSharp.API/Options.cs @@ -14,15 +14,15 @@ public enum ExecutionMode /// /// /// - public enum SearchMode + public enum SearchStrategy { DFS, BFS, - Guided + ShortestDistance } /// - /// + /// /// public enum CoverageZone { @@ -35,23 +35,27 @@ public enum CoverageZone /// /// /// Directory to place generated *.vst tests. If null or empty, process working directory is used. - /// + /// /// /// + /// /// + /// Timeout for code exploration in seconds. Negative value means infinite timeout (up to exhaustive coverage or user interruption). /// /// public readonly record struct CoverOptions( string OutputDirectory = "", - SearchMode SearchMode = SearchMode.Guided, + SearchStrategy SearchStrategy = SearchStrategy.ShortestDistance, CoverageZone CoverageZone = CoverageZone.Method, ExecutionMode ExecutionMode = ExecutionMode.Symbolic, + bool GuidedSearch = false, uint RecThreshold = 0u, + int Timeout = -1, bool IsConstraintIndependenceEnabled = true, bool IsSolverIncrementalityEnabled = false ) { - internal siliOptions toSiliOptions() + internal SiliOptions ToSiliOptions() { var coverageZone = CoverageZone switch { @@ -60,24 +64,30 @@ internal siliOptions toSiliOptions() CoverageZone.Module => Interpreter.IL.coverageZone.ModuleZone }; - var searchMode = SearchMode switch + var searchMode = SearchStrategy switch { - SearchMode.DFS => Interpreter.IL.searchMode.DFSMode, - SearchMode.BFS => Interpreter.IL.searchMode.BFSMode, - SearchMode.Guided => Interpreter.IL.searchMode.GuidedMode + SearchStrategy.DFS => Interpreter.IL.searchMode.DFSMode, + SearchStrategy.BFS => Interpreter.IL.searchMode.BFSMode, + SearchStrategy.ShortestDistance => Interpreter.IL.searchMode.ShortestDistanceBasedMode }; + if (GuidedSearch) + { + searchMode = searchMode.NewGuidedMode(searchMode); + } + var executionMode = ExecutionMode switch { ExecutionMode.Symbolic => Interpreter.IL.executionMode.SymbolicMode, ExecutionMode.Concolic => Interpreter.IL.executionMode.ConcolicMode }; - return new siliOptions( + return new SiliOptions( OutputDirectory, explorationMode.NewTestCoverageMode(coverageZone, searchMode), executionMode, - RecThreshold + RecThreshold, + Timeout ); } } diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index 756ad8a29..ad15dd5d5 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -71,10 +71,10 @@ public void GenerateReport(TextWriter writer) public static class TestGenerator { - private static Statistics StartExploration(List methods, string resultsFolder, string[] mainArguments = null, int timeout = -1) + private static Statistics StartExploration(List methods, CoverOptions options = new(), string[] mainArguments = null) { // TODO: customize search strategies via console options - SILI explorer = new SILI(options.toSiliOptions()); + SILI explorer = new SILI(options.ToSiliOptions()); UnitTests unitTests = new UnitTests(options.OutputDirectory); Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled); @@ -110,9 +110,9 @@ public static bool Reproduce(DirectoryInfo testDir) /// Generates test coverage for specified method. /// /// Type to be covered with tests. - /// Timeout for code exploration in seconds. Negative value means infinite timeout (up to exhaustive coverage or user interuption). + /// Additional parameters of the run. /// Summary of tests generation process. - public static Statistics Cover(MethodBase method, int timeout = -1, string outputDirectory = "") + public static Statistics Cover(MethodBase method, CoverOptions options = new()) { List methods = new List {method}; return StartExploration(methods, options); @@ -122,10 +122,10 @@ public static Statistics Cover(MethodBase method, int timeout = -1, string outpu /// Generates test coverage for all public methods of specified type. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Additional parameters of the run. /// Summary of tests generation process. /// Thrown if specified class does not contain public methods. - public static Statistics Cover(Type type, int timeout = -1, string outputDirectory = "") + public static Statistics Cover(Type type, CoverOptions options = new()) { List methods = new List(type.GetConstructors()); @@ -145,11 +145,11 @@ public static Statistics Cover(Type type, int timeout = -1, string outputDirecto /// Generates test coverage for all public methods of all public classes in the specified assembly. /// /// Assembly to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Additional parameters of the run. /// Summary of tests generation process. /// Thrown if no public methods found in assembly. /// - public static Statistics Cover(Assembly assembly, int timeout = -1, string outputDirectory = "") + public static Statistics Cover(Assembly assembly, CoverOptions options = new()) { List methods; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | @@ -178,12 +178,12 @@ public static Statistics Cover(Assembly assembly, int timeout = -1, string outpu /// Generates test coverage for the entry point of the specified assembly. /// /// Assembly to be covered with tests. - /// Command line arguments of entry point - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Command line arguments of entry point. + /// Additional parameters of the run. /// Summary of tests generation process. /// Thrown if assembly does not contain entry point. /// - public static Statistics Cover(Assembly assembly, string[] args, int timeout = -1, string outputDirectory = "") + public static Statistics Cover(Assembly assembly, string[] args, CoverOptions options = new()) { List methods; var entryPoint = assembly.EntryPoint; @@ -200,9 +200,9 @@ public static Statistics Cover(Assembly assembly, string[] args, int timeout = - /// Generates test coverage for the specified method and runs all tests. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Additional parameters of the run. /// True if all generated tests have passed. - public static bool CoverAndRun(MethodBase method, int timeout = -1, string outputDirectory = "") + public static bool CoverAndRun(MethodBase method, CoverOptions options = new()) { var stats = Cover(method, options); return Reproduce(stats.OutputDir); @@ -212,10 +212,10 @@ public static bool CoverAndRun(MethodBase method, int timeout = -1, string outpu /// Generates test coverage for the specified type and runs all tests. /// /// Type to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Additional parameters of the run. /// True if all generated tests have passed. /// Thrown if specified class does not contain public methods. - public static bool CoverAndRun(Type type, int timeout = -1, string outputDirectory = "") + public static bool CoverAndRun(Type type, CoverOptions options = new()) { var stats = Cover(type, options); return Reproduce(stats.OutputDir); @@ -225,11 +225,11 @@ public static bool CoverAndRun(Type type, int timeout = -1, string outputDirecto /// Generates test coverage for all public methods of all public classes of the specified assembly and runs all tests. /// /// Assembly to be covered with tests. - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Additional parameters of the run. /// True if all generated tests have passed. /// Thrown if no public methods found in assembly. /// - public static bool CoverAndRun(Assembly assembly, int timeout = -1, string outputDirectory = "") + public static bool CoverAndRun(Assembly assembly, CoverOptions options = new()) { var stats = Cover(assembly, options); return Reproduce(stats.OutputDir); @@ -239,11 +239,11 @@ public static bool CoverAndRun(Assembly assembly, int timeout = -1, string outpu /// Generates test coverage for entry point of the specified assembly and runs all tests. /// /// Assembly to be covered with tests. - /// Command line arguments of entry point - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Command line arguments of entry point. + /// Additional parameters of the run. /// True if all generated tests have passed. /// Thrown if assembly does not contain entry point. - public static bool CoverAndRun(Assembly assembly, string[] args, int timeout = -1, string outputDirectory = "") + public static bool CoverAndRun(Assembly assembly, string[] args, CoverOptions options = new()) { var stats = Cover(assembly, args, options); return Reproduce(stats.OutputDir); diff --git a/VSharp.Runner/RunnerProgram.cs b/VSharp.Runner/RunnerProgram.cs index 7f914f78d..79d376e74 100644 --- a/VSharp.Runner/RunnerProgram.cs +++ b/VSharp.Runner/RunnerProgram.cs @@ -115,7 +115,7 @@ public static int Main(string[] args) var timeoutOption = new Option(aliases: new[] { "--timeout", "-t" }, () => -1, - "Time for test generation. Negative values mean no timeout."); + "Time for test generation. Negative values mean no timeout"); var outputOption = new Option(aliases: new[] { "--output", "-o" }, () => new DirectoryInfo(Directory.GetCurrentDirectory()), @@ -182,9 +182,9 @@ public static int Main(string[] args) rootCommand.Description = "Symbolic execution engine for .NET"; - entryPointCommand.Handler = CommandHandler.Create + entryPointCommand.Handler = CommandHandler.Create ( - (assemblyPath, args, output, unknownArgs, cIndependence, incrementality) => + (assemblyPath, args, timeout, output, unknownArgs, cIndependence, incrementality) => { var assembly = ResolveAssembly(assemblyPath); if (unknownArgs) @@ -194,16 +194,17 @@ public static int Main(string[] args) var options = new CoverOptions( OutputDirectory: output.FullName, IsConstraintIndependenceEnabled: cIndependence, - IsSolverIncrementalityEnabled: incrementality + IsSolverIncrementalityEnabled: incrementality, + Timeout: timeout ); PostProcess(TestGenerator.Cover(assembly, args, options)); } } ); - allPublicMethodsCommand.Handler = CommandHandler.Create + allPublicMethodsCommand.Handler = CommandHandler.Create ( - (assemblyPath, output, cIndependence, incrementality) => + (assemblyPath, timeout, output, cIndependence, incrementality) => { var assembly = ResolveAssembly(assemblyPath); if (assembly != null) @@ -211,16 +212,17 @@ public static int Main(string[] args) var options = new CoverOptions( OutputDirectory: output.FullName, IsConstraintIndependenceEnabled: cIndependence, - IsSolverIncrementalityEnabled: incrementality + IsSolverIncrementalityEnabled: incrementality, + Timeout: timeout ); PostProcess(TestGenerator.Cover(assembly, options)); } } ); - publicMethodsOfClassCommand.Handler = CommandHandler.Create + publicMethodsOfClassCommand.Handler = CommandHandler.Create ( - (className, assemblyPath, output, cIndependence, incrementality) => + (className, assemblyPath, timeout, output, cIndependence, incrementality) => { var assembly = ResolveAssembly(assemblyPath); if (assembly != null) @@ -231,7 +233,8 @@ public static int Main(string[] args) var options = new CoverOptions( OutputDirectory: output.FullName, IsConstraintIndependenceEnabled: cIndependence, - IsSolverIncrementalityEnabled: incrementality + IsSolverIncrementalityEnabled: incrementality, + Timeout: timeout ); PostProcess(TestGenerator.Cover(type, options)); @@ -239,9 +242,9 @@ public static int Main(string[] args) } } ); - specificMethodCommand.Handler = CommandHandler.Create + specificMethodCommand.Handler = CommandHandler.Create ( - (methodName, assemblyPath, output, cIndependence, incrementality) => + (methodName, assemblyPath, timeout, output, cIndependence, incrementality) => { var assembly = ResolveAssembly(assemblyPath); if (assembly != null) @@ -252,7 +255,8 @@ public static int Main(string[] args) var options = new CoverOptions( OutputDirectory: output.FullName, IsConstraintIndependenceEnabled: cIndependence, - IsSolverIncrementalityEnabled: incrementality + IsSolverIncrementalityEnabled: incrementality, + Timeout: timeout ); PostProcess(TestGenerator.Cover(method, options)); diff --git a/VSharp.SILI.Core/API.fs b/VSharp.SILI.Core/API.fs index ffbef92f7..9207dffa3 100644 --- a/VSharp.SILI.Core/API.fs +++ b/VSharp.SILI.Core/API.fs @@ -169,7 +169,7 @@ module API = let AddConstraint conditionState condition = Memory.addConstraint conditionState condition let IsFalsePathCondition conditionState = conditionState.pc.IsFalse let Contradicts state condition = - let copy = PC.add condition state.pc + let copy = PC.add state.pc condition copy.IsFalse let PathConditionToSeq (pc : IPathCondition) = pc.ToSeq() let EmptyPathCondition() = PC.create() @@ -277,9 +277,9 @@ module API = let EmptyStack = EvaluationStack.empty module public Memory = - let EmptyState() = Memory.makeEmpty false + let EmptyState() = State.makeEmpty false let EmptyModel method = - let modelState = Memory.makeEmpty true + let modelState = State.makeEmpty true Memory.fillWithParametersAndThis modelState method {subst = Dictionary<_,_>(); state = modelState} diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 6b7476656..ce8ebd673 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -212,7 +212,6 @@ module API = module public Memory = val EmptyState : unit -> state - val EmptyStateWithModel : state -> state val EmptyModel : MethodBase -> model val PopFrame : state -> unit val ForcePopFrames : int -> state -> unit diff --git a/VSharp.SILI.Core/Branching.fs b/VSharp.SILI.Core/Branching.fs index 9bbb77646..dda73122c 100644 --- a/VSharp.SILI.Core/Branching.fs +++ b/VSharp.SILI.Core/Branching.fs @@ -1,3 +1,4 @@ +(* namespace VSharp.Core open System.Collections.Generic @@ -185,12 +186,22 @@ module internal Branching = executionWithConstraintIndependence true copy state conditionInvocation thenBranch elseBranch merge2Results k else executionWithoutConstraintIndependence true copy state conditionInvocation thenBranch elseBranch merge2Results k +*) namespace VSharp.Core open VSharp -module Branching = +module internal Branching = + + let private keepDependentWith (pc : IPathCondition) cond = + if FeatureFlags.isConstraintIndependenceEnabled() then + pc.Fragments + |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) + |> Option.defaultValue pc + else + pc + let checkSat state = TypeCasting.checkSatWithSubtyping state let commonGuardedStatedApplyk f state term mergeResults k = @@ -198,7 +209,7 @@ module Branching = | Union gvs -> let filterUnsat (g, v) k = let pc = PC.add state.pc g - if PC.isFalse pc then k None + if pc.IsFalse then k None else Some (pc, v) |> k Cps.List.choosek filterUnsat gvs (fun pcs -> match pcs with @@ -251,11 +262,13 @@ module Branching = | Some model -> model.Eval condition | None -> __unreachable__() if isTrue evaled then - let elsePc = PC.add pc !!condition - if PC.isFalse elsePc then + let notCondition = !!condition + let elsePc = PC.add pc notCondition + if elsePc.IsFalse then thenBranch conditionState (List.singleton >> k) elif not branchesReleased then - conditionState.pc <- elsePc + let independentElsePc = keepDependentWith elsePc notCondition + conditionState.pc <- independentElsePc match checkSat conditionState with | SolverInteraction.SmtUnsat _ -> conditionState.pc <- pc @@ -275,10 +288,11 @@ module Branching = elif isFalse evaled then let notCondition = !!condition let thenPc = PC.add state.pc condition - if PC.isFalse thenPc then + if thenPc.IsFalse then elseBranch conditionState (List.singleton >> k) elif not branchesReleased then - conditionState.pc <- thenPc + let independentThenPc = keepDependentWith thenPc condition + conditionState.pc <- independentThenPc match checkSat conditionState with | SolverInteraction.SmtUnsat _ -> conditionState.pc <- pc @@ -290,7 +304,7 @@ module Branching = let thenState = conditionState let elseState = Memory.copy conditionState (PC.add pc notCondition) thenState.model <- Some model.mdl - elseState.pc <- PC.add pc notCondition + thenState.pc <- thenPc execution thenState elseState condition k else conditionState.pc <- PC.add pc notCondition diff --git a/VSharp.SILI.Core/Copying.fs b/VSharp.SILI.Core/Copying.fs index aec598922..932e77b4a 100644 --- a/VSharp.SILI.Core/Copying.fs +++ b/VSharp.SILI.Core/Copying.fs @@ -24,7 +24,8 @@ module internal Copying = let constant = Constant "i" source Types.Int32 let leftBound = simplifyLessOrEqual lowerBound constant id let rightBound = simplifyLessOrEqual constant upperBound id - let pcWithBounds = PC.add leftBound state.pc |> PC.add rightBound + let pcWithBounds = PC.add state.pc leftBound + pcWithBounds.Add rightBound state.pc <- pcWithBounds constant diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index 2c6314a95..ab31403e1 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -26,12 +26,12 @@ module internal Memory = let mutable memoryMode = SymbolicMemory - let copy state = + let copy state newPc = let state = match memoryMode with | ConcreteMemory -> ConcreteMemory.deepCopy state | SymbolicMemory -> state - { state with id = Guid.NewGuid().ToString() } + { state with id = Guid.NewGuid().ToString(); pc = newPc } let private isZeroAddress (x : concreteHeapAddress) = x = VectorTime.zero @@ -366,21 +366,6 @@ module internal Memory = else parameters newStackFrame state method parametersAndThis - - let fillWithParametersAndThis state (method : System.Reflection.MethodBase) = - let parameters = method.GetParameters() |> Seq.map (fun param -> - (ParameterKey param, None, fromDotNetType param.ParameterType)) |> List.ofSeq - let parametersAndThis = - if Reflection.hasThis method then - let t = fromDotNetType method.DeclaringType - let addr = [-1] - let thisRef = HeapRef (ConcreteHeapAddress addr) t - state.allocatedTypes <- PersistentDict.add addr t state.allocatedTypes - state.startingTime <- [-2] - (ThisKey method, Some thisRef, t) :: parameters // TODO: incorrect type when ``this'' is Ref to stack - else parameters - newStackFrame state method parametersAndThis - // =============== Marshalling/unmarshalling without state changing =============== // ------------------ Object to term ------------------ @@ -683,7 +668,7 @@ module internal Memory = and private readStructUnsafe fields structType startByte endByte = let readField fieldId = fields.[fieldId] - readFieldsUnsafe (State.makeEmpty()) (fun _ -> __unreachable__()) readField false structType startByte endByte + readFieldsUnsafe (State.makeEmpty false) (fun _ -> __unreachable__()) readField false structType startByte endByte and private getAffectedFields state reportError readField isStatic (blockType : symbolicType) startByte endByte = let t = toDotNetType blockType @@ -1010,7 +995,7 @@ module internal Memory = and private writeStructUnsafe structTerm fields structType startByte value = let readField fieldId = fields.[fieldId] - let updatedFields = writeFieldsUnsafe (State.makeEmpty()) (fun _ -> __unreachable__()) readField false structType startByte value + let updatedFields = writeFieldsUnsafe (State.makeEmpty false) (fun _ -> __unreachable__()) readField false structType startByte value let writeField structTerm (fieldId, value) = writeStruct structTerm fieldId value List.fold writeField structTerm updatedFields @@ -1417,7 +1402,7 @@ module internal Memory = if not <| isFalse g then return { id = Guid.NewGuid().ToString() - pc = if isTrue g then pc else PC.add g pc + pc = if isTrue g then pc else PC.add pc g evaluationStack = evaluationStack exceptionsRegister = exceptionRegister stack = stack diff --git a/VSharp.SILI.Core/Merging.fs b/VSharp.SILI.Core/Merging.fs index 01ed8cb76..02991715c 100644 --- a/VSharp.SILI.Core/Merging.fs +++ b/VSharp.SILI.Core/Merging.fs @@ -104,7 +104,7 @@ module internal Merging = let commonGuardedMapkWithPC (pc : IPathCondition) mapper gvs merge k = let foldFunc gvs (g, v) k = - let pc' = PC.add g pc + let pc' = PC.add pc g if pc'.IsFalse then k gvs else mapper v (fun t -> k ((g, t) :: gvs)) Cps.List.foldlk foldFunc [] gvs (merge >> k) diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 4a1e1c312..616761b16 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -265,7 +265,7 @@ module internal PC = member this.Constants = constants.Keys - let public add newConstraint (pc : IPathCondition) = + let public add (pc : IPathCondition) newConstraint = let copy = pc.Copy() copy.Add newConstraint copy diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index b787b8184..3c4ba684c 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -20,9 +20,9 @@ module public SolverInteraction = | SmtUnknown of string type ISolver = - abstract CheckSat : encodingContext -> term -> smtResult + abstract CheckSat : encodingContext -> term -> model -> smtResult abstract Assert : encodingContext -> term -> unit - abstract CheckAssumptions : encodingContext -> model -> formula seq -> smtResult + abstract CheckAssumptions : encodingContext -> term seq -> model -> smtResult let mutable private mSolver : ISolver option = None let mutable private isIncrementalModeEnabled : bool = false @@ -38,20 +38,20 @@ module public SolverInteraction = let orderWithNull = Map.add VectorTime.zero 0 order { addressOrder = orderWithNull } - let private getOrEmpty = Option.defaultValue { state = State.makeEmpty(); subst = Dictionary<_, _>(); complete = true } + let private getOrEmpty = Option.defaultValue { state = State.makeEmpty true; subst = Dictionary<_, _>() } let private checkSatPlainly state = let ctx = getEncodingContext state let formula = state.pc.ToSeq() |> conjunction match mSolver with - | Some s -> s.CheckSat ctx { lvl = Level.zero; queryFml = formula; currentModel = getOrEmpty state.model } + | Some s -> s.CheckSat ctx formula (getOrEmpty state.model) | None -> SmtUnknown "Solver not configured" let private checkSatIncrementally state = let ctx = getEncodingContext state let conditions = state.pc |> PC.toSeq match mSolver with - | Some s -> s.CheckAssumptions ctx (getOrEmpty state.model) conditions + | Some s -> s.CheckAssumptions ctx conditions (getOrEmpty state.model) | None -> SmtUnknown "Solver not configured" let checkSat state = diff --git a/VSharp.SILI.Core/State.fs b/VSharp.SILI.Core/State.fs index 2311b9fb3..7de1def01 100644 --- a/VSharp.SILI.Core/State.fs +++ b/VSharp.SILI.Core/State.fs @@ -3,7 +3,6 @@ namespace VSharp.Core open System open System.Collections.Generic open VSharp -open VSharp.Core.Types.Constructor open VSharp.Utils type typeVariables = mappedStack * typeId list stack @@ -89,28 +88,29 @@ with and [] state = { - mutable pc : pathCondition - mutable evaluationStack : evaluationStack - mutable stack : callStack // Arguments and local variables - mutable stackBuffers : pdict // Buffers allocated via stackAlloc - mutable classFields : pdict // Fields of classes in heap - mutable arrays : pdict // Contents of arrays in heap - mutable lengths : pdict // Lengths by dimensions of arrays in heap - mutable lowerBounds : pdict // Lower bounds by dimensions of arrays in heap - mutable staticFields : pdict // Static fields of types without type variables - mutable boxedLocations : pdict // Value types boxed in heap - mutable initializedTypes : symbolicTypeSet // Types with initialized static members - concreteMemory : concreteMemory // Fully concrete objects - mutable physToVirt : pdict // Map from physical address (obj) to concreteHeapAddress - mutable allocatedTypes : pdict // Types of heap locations allocated via new - mutable typeVariables : typeVariables // Type variables assignment in the current state - mutable delegates : pdict // Subtypes of System.Delegate allocated in heap - mutable currentTime : vectorTime // Current timestamp (and next allocated address as well) in this state - mutable startingTime : vectorTime // Timestamp before which all allocated addresses will be considered symbolic - mutable exceptionsRegister : exceptionRegister // Heap-address of exception object - mutable model : model option // Concrete valuation of symbolics - complete : bool // If true, reading of undefined locations would result in default values - } + id : string // Unique identifier for state tracking + mutable pc : IPathCondition + mutable evaluationStack : evaluationStack + mutable stack : callStack // Arguments and local variables + mutable stackBuffers : pdict // Buffers allocated via stackAlloc + mutable classFields : pdict // Fields of classes in heap + mutable arrays : pdict // Contents of arrays in heap + mutable lengths : pdict // Lengths by dimensions of arrays in heap + mutable lowerBounds : pdict // Lower bounds by dimensions of arrays in heap + mutable staticFields : pdict // Static fields of types without type variables + mutable boxedLocations : pdict // Value types boxed in heap + mutable initializedTypes : symbolicTypeSet // Types with initialized static members + concreteMemory : concreteMemory // Fully concrete objects + mutable physToVirt : pdict // Map from physical address (obj) to concreteHeapAddress + mutable allocatedTypes : pdict // Types of heap locations allocated via new + mutable typeVariables : typeVariables // Type variables assignment in the current state + mutable delegates : pdict // Subtypes of System.Delegate allocated in heap + mutable currentTime : vectorTime // Current timestamp (and next allocated address as well) in this state + mutable startingTime : vectorTime // Timestamp before which all allocated addresses will be considered symbolic + mutable exceptionsRegister : exceptionRegister // Heap-address of exception object + mutable model : model option // Concrete valuation of symbolics + complete : bool // If true, reading of undefined locations would result in default values + } and IStatedSymbolicConstantSource = @@ -119,7 +119,7 @@ and module public State = - let private makeEmptyInternal modelState = { + let makeEmpty complete = { id = Guid.NewGuid().ToString() pc = PC.create() evaluationStack = EvaluationStack.empty @@ -140,10 +140,6 @@ module public State = delegates = PersistentDict.empty currentTime = [1] startingTime = VectorTime.zero - model = - Option.bind (fun state -> Some {subst = Dictionary<_,_>(); state = state; complete = true}) modelState + model = None + complete = complete } - - let makeEmptyWithModel modelState = makeEmptyInternal (Some modelState) - - let makeEmpty() = makeEmptyInternal None diff --git a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj index 15c3797ce..d4a68c336 100644 --- a/VSharp.SILI.Core/VSharp.SILI.Core.fsproj +++ b/VSharp.SILI.Core/VSharp.SILI.Core.fsproj @@ -35,7 +35,6 @@ - diff --git a/VSharp.SILI/SILI.fs b/VSharp.SILI/SILI.fs index 711049d6d..52da92c04 100644 --- a/VSharp.SILI/SILI.fs +++ b/VSharp.SILI/SILI.fs @@ -13,7 +13,7 @@ open CilStateOperations open VSharp.Interpreter.IL open VSharp.Solver -type public SILI(options : siliOptions) = +type public SILI(options : SiliOptions) = let stopwatch = Stopwatch() let () = stopwatch.Start() @@ -71,7 +71,7 @@ type public SILI(options : siliOptions) = let coveragePobsForMethod (method : MethodBase) = let cfg = Application.applicationGraph.GetCfg method cfg.SortedOffsets |> Seq.map (fun offset -> - {loc = {offset = offset; method = method}; lvl = infty; pc = EmptyPathCondition}) + {loc = {offset = offset; method = method}; lvl = infty; pc = EmptyPathCondition()}) |> List.ofSeq let reportState reporter isError method cmdArgs state = diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 2c12b2625..025cd3786 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -664,7 +664,7 @@ module internal Z3 = encodingCache.termToExpression |> Seq.iter (fun kvp -> match kvp.Key with | {term = Constant(_, StructFieldChain(fields, StackReading(key)), t)} as constant -> - let refinedExpr = m.Eval(kvp.Value.expr, false) + let refinedExpr = z3Model.Eval(kvp.Value.expr, false) let decoded = x.Decode t refinedExpr if decoded <> constant then x.WriteDictOfValueTypes stackEntries key fields key.TypeOfLocation decoded @@ -697,7 +697,7 @@ module internal Z3 = encodingCache.regionConstants |> Seq.iter (fun kvp -> let region, fields = kvp.Key let constant = kvp.Value - let arr = m.Eval(constant, false) + let arr = z3Model.Eval(constant, false) let typeOfLocation = if fields.IsEmpty then region.TypeOfLocation else fields.Head.typ |> Types.FromDotNetType @@ -766,7 +766,7 @@ module internal Z3 = // pathAtoms interface ISolver with - member x.CheckSat (encCtx : encodingContext) (q : term) : smtResult = + member x.CheckSat (encCtx : encodingContext) (q : term) (currentModel : model) : smtResult = printLog Trace "SOLVER: trying to solve constraints..." printLogLazy Trace "%s" (lazy(q.ToString())) try @@ -783,7 +783,7 @@ module internal Z3 = | Status.SATISFIABLE -> trace "SATISFIABLE" let z3Model = optCtx.Model - let updatedModel = {q.currentModel with state = {q.currentModel.state with model = q.currentModel.state.model}} + let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} builder.UpdateModel z3Model updatedModel builder.ClearTermToExpressionCache() SmtSat { mdl = updatedModel } @@ -807,7 +807,7 @@ module internal Z3 = let encoded = List.fold (fun acc x -> builder.MkAnd(acc, x)) (encoded.expr :?> BoolExpr) encoded.assumptions optCtx.Assert(encoded) - member x.CheckAssumptions encCtx currentModel formulas = + member x.CheckAssumptions encCtx formulas currentModel = let encodeToBoolExprs formula = let encoded = builder.EncodeTerm encCtx formula seq { @@ -839,7 +839,7 @@ module internal Z3 = let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} builder.UpdateModel z3Model updatedModel builder.ClearTermToExpressionCache() - SmtSat { mdl = updatedModel; usedPaths = [] } + SmtSat { mdl = updatedModel } | Status.UNSATISFIABLE -> builder.ClearTermToExpressionCache() SmtUnsat { core = Array.empty } diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 07f7d1e8a..20b3ec238 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -15,13 +15,6 @@ namespace VSharp.Test { - public enum SearchStrategy - { - DFS, - BFS, - ShortestDistance - } - public class TestSvmFixtureAttribute : NUnitAttribute, IFixtureBuilder { public IEnumerable BuildFrom(ITypeInfo typeInfo) @@ -33,7 +26,7 @@ public IEnumerable BuildFrom(ITypeInfo typeInfo) public class TestSvmAttribute : NUnitAttribute, IWrapTestMethod, ISimpleTestBuilder { - private static siliOptions _options = null; + private static SiliOptions _options = null; static TestSvmAttribute() { diff --git a/VSharp.Test/Tests/LoanExam/CreditCalculator.cs b/VSharp.Test/Tests/LoanExam/CreditCalculator.cs index 3a4d29455..929d5b953 100644 --- a/VSharp.Test/Tests/LoanExam/CreditCalculator.cs +++ b/VSharp.Test/Tests/LoanExam/CreditCalculator.cs @@ -1,6 +1,5 @@ -using InstantCredit.Shared.Models.Enums; -using LoanExam.Models; using System; +using VSharp; using VSharp.Test; namespace LoanExam; diff --git a/VSharp.Test/VSharp.Test.csproj b/VSharp.Test/VSharp.Test.csproj index a7c6b94ab..db96cb7c7 100644 --- a/VSharp.Test/VSharp.Test.csproj +++ b/VSharp.Test/VSharp.Test.csproj @@ -40,6 +40,7 @@ + From 49b281c65324d7929cf9e20b83cb9326cd0a79ed Mon Sep 17 00:00:00 2001 From: mxprshn Date: Fri, 22 Jul 2022 16:14:20 +0300 Subject: [PATCH 80/88] [fix] Fix FillWithParametersAndThis signature --- VSharp.SILI.Core/API.fsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSharp.SILI.Core/API.fsi b/VSharp.SILI.Core/API.fsi index 04478a43a..f1ecc9495 100644 --- a/VSharp.SILI.Core/API.fsi +++ b/VSharp.SILI.Core/API.fsi @@ -245,7 +245,7 @@ module API = val MakeSymbolicThis : IMethod -> term val MakeSymbolicValue : IMemoryAccessConstantSource -> string -> symbolicType -> term - val FillWithParametersAndThis : state -> MethodBase -> unit + val FillWithParametersAndThis : state -> IMethod -> unit val CallStackContainsFunction : state -> IMethod -> bool val CallStackSize : state -> int From 5d624a60234b3a11208141918e8dce28c140e42f Mon Sep 17 00:00:00 2001 From: mxprshn Date: Mon, 25 Jul 2022 23:09:14 +0300 Subject: [PATCH 81/88] [fix] Fix 'visualize' option conflict --- VSharp.API/Options.cs | 16 ++++++++++------ VSharp.API/VSharp.cs | 2 +- VSharp.Runner/RunnerProgram.cs | 8 ++++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/VSharp.API/Options.cs b/VSharp.API/Options.cs index 0fc767958..d3afb41b7 100644 --- a/VSharp.API/Options.cs +++ b/VSharp.API/Options.cs @@ -1,4 +1,5 @@ -using VSharp.Interpreter.IL; +using System.IO; +using VSharp.Interpreter.IL; namespace VSharp { @@ -43,16 +44,18 @@ public enum CoverageZone /// Timeout for code exploration in seconds. Negative value means infinite timeout (up to exhaustive coverage or user interruption). /// /// + /// public readonly record struct CoverOptions( - string OutputDirectory = "", - SearchStrategy SearchStrategy = SearchStrategy.ShortestDistance, + DirectoryInfo OutputDirectory = null, + SearchStrategy SearchStrategy = SearchStrategy.DFS, CoverageZone CoverageZone = CoverageZone.Method, ExecutionMode ExecutionMode = ExecutionMode.Symbolic, bool GuidedSearch = false, uint RecThreshold = 0u, int Timeout = -1, bool IsConstraintIndependenceEnabled = true, - bool IsSolverIncrementalityEnabled = false + bool IsSolverIncrementalityEnabled = false, + bool Visualize = false ) { internal SiliOptions ToSiliOptions() @@ -83,11 +86,12 @@ internal SiliOptions ToSiliOptions() }; return new SiliOptions( - OutputDirectory, explorationMode.NewTestCoverageMode(coverageZone, searchMode), executionMode, + OutputDirectory, RecThreshold, - Timeout + Timeout, + Visualize ); } } diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index ad15dd5d5..65691aa40 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -74,8 +74,8 @@ public static class TestGenerator private static Statistics StartExploration(List methods, CoverOptions options = new(), string[] mainArguments = null) { // TODO: customize search strategies via console options + UnitTests unitTests = new UnitTests(options.OutputDirectory.FullName); SILI explorer = new SILI(options.ToSiliOptions()); - UnitTests unitTests = new UnitTests(options.OutputDirectory); Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled); diff --git a/VSharp.Runner/RunnerProgram.cs b/VSharp.Runner/RunnerProgram.cs index 79d376e74..5a0bd46ed 100644 --- a/VSharp.Runner/RunnerProgram.cs +++ b/VSharp.Runner/RunnerProgram.cs @@ -192,7 +192,7 @@ public static int Main(string[] args) if (assembly != null) { var options = new CoverOptions( - OutputDirectory: output.FullName, + OutputDirectory: output, IsConstraintIndependenceEnabled: cIndependence, IsSolverIncrementalityEnabled: incrementality, Timeout: timeout @@ -210,7 +210,7 @@ public static int Main(string[] args) if (assembly != null) { var options = new CoverOptions( - OutputDirectory: output.FullName, + OutputDirectory: output, IsConstraintIndependenceEnabled: cIndependence, IsSolverIncrementalityEnabled: incrementality, Timeout: timeout @@ -231,7 +231,7 @@ public static int Main(string[] args) if (type != null) { var options = new CoverOptions( - OutputDirectory: output.FullName, + OutputDirectory: output, IsConstraintIndependenceEnabled: cIndependence, IsSolverIncrementalityEnabled: incrementality, Timeout: timeout @@ -253,7 +253,7 @@ public static int Main(string[] args) if (method != null) { var options = new CoverOptions( - OutputDirectory: output.FullName, + OutputDirectory: output, IsConstraintIndependenceEnabled: cIndependence, IsSolverIncrementalityEnabled: incrementality, Timeout: timeout From d71e132fe660ccfad3fd2e8be70441fa5fec6784 Mon Sep 17 00:00:00 2001 From: mxprshn Date: Mon, 25 Jul 2022 23:14:58 +0300 Subject: [PATCH 82/88] [fix] Fix broken import --- VSharp.Test/IntegrationTests.cs | 1 - VSharp.Test/Tests/LoanExam/CreditCalculator.cs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index 865c904c5..e780ea52d 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -127,7 +127,6 @@ private TestResult Explore(TestExecutionContext context) { UnitTests unitTests = new UnitTests(Directory.GetCurrentDirectory()); _options = new SiliOptions( - "", explorationMode.NewTestCoverageMode(coverageZone.MethodZone, _searchStrat), _executionMode, unitTests.TestDirectory, diff --git a/VSharp.Test/Tests/LoanExam/CreditCalculator.cs b/VSharp.Test/Tests/LoanExam/CreditCalculator.cs index 929d5b953..e8d615cb2 100644 --- a/VSharp.Test/Tests/LoanExam/CreditCalculator.cs +++ b/VSharp.Test/Tests/LoanExam/CreditCalculator.cs @@ -1,3 +1,5 @@ +using InstantCredit.Shared.Models.Enums; +using LoanExam.Models; using System; using VSharp; using VSharp.Test; From d077c56cf4e4aa9ce1385b9d2ec509356ac1dab6 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Mon, 1 Aug 2022 20:26:17 +0300 Subject: [PATCH 83/88] [fix] Bug fixes: incorrect vector time in encoding cache, incorrect IndependentWith in struct field source, multiple frames in model --- VSharp.SILI.Core/Branching.fs | 190 ------------------------------ VSharp.SILI.Core/Memory.fs | 2 +- VSharp.SILI.Core/PathCondition.fs | 10 +- VSharp.Solver/Z3.fs | 17 +-- VSharp.Test/Tests/Lists.cs | 3 +- VSharp.Utils/TaggedLogger.fs | 2 +- 6 files changed, 19 insertions(+), 205 deletions(-) diff --git a/VSharp.SILI.Core/Branching.fs b/VSharp.SILI.Core/Branching.fs index dda73122c..412d8c8d6 100644 --- a/VSharp.SILI.Core/Branching.fs +++ b/VSharp.SILI.Core/Branching.fs @@ -1,193 +1,3 @@ -(* -namespace VSharp.Core - -open System.Collections.Generic -open VSharp - -module internal Branching = - - let private withPc newPc state = { state with pc = newPc } - - let private noNewConstants condition (pc : IPathCondition) = - let pcConstants = HashSet(pc.Constants) - let condConstants = condition |> Seq.singleton |> discoverConstants - pcConstants.IsSupersetOf condConstants - - let private keepDependentWith (pc : IPathCondition) cond = - pc.Fragments - |> Seq.tryFind (fun pc -> pc.ToSeq() |> Seq.contains cond) - |> Option.defaultValue pc - - let private executionWithConstraintIndependence tryEval copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let negatedCondition = !!condition - let thenPc = PC.add condition state.pc - let elsePc = PC.add negatedCondition state.pc - if thenPc.IsFalse then - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - elif elsePc.IsFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - else - let independentThenPc = keepDependentWith thenPc condition - // In fact, this call is redundant because independentElsePc == independentThenPc with negated cond - let independentElsePc = keepDependentWith elsePc negatedCondition - let checkBothBranches () = - conditionState.pc <- independentThenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- elsePc - conditionState.model <- Some elseModel.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- independentElsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.model <- Some thenModel.mdl - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState |> withPc elsePc - elseState.model <- Some elseModel.mdl - thenState.pc <- thenPc - execution thenState elseState condition k - if tryEval && noNewConstants condition state.pc then - let evaluatedCondition = state.model.Value.Eval condition - // Current model satisfies new condition, so we can keep it for 'then' branch - if isTrue evaluatedCondition then - let elseState = copy conditionState |> withPc independentElsePc - conditionState.pc <- thenPc - match SolverInteraction.checkSat elseState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - elseState.pc <- elsePc - elseState.model <- Some elseModel.mdl - execution conditionState elseState condition k - // Current model satisfies !condition, so we can keep it for 'else' branch - elif isFalse evaluatedCondition then - let thenState = copy conditionState |> withPc independentThenPc - match SolverInteraction.checkSat thenState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - let elseState = copy conditionState |> withPc elsePc - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - execution conditionState elseState condition k - else - checkBothBranches() - else - checkBothBranches()) - - let private executionWithoutConstraintIndependence tryEval copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - let execution thenState elseState condition k = - assert (condition <> True && condition <> False) - thenBranch thenState (fun thenResult -> - elseBranch elseState (fun elseResult -> - merge2Results thenResult elseResult |> k)) - conditionInvocation state (fun (condition, conditionState) -> - let negatedCondition = !!condition - let thenPc = PC.add condition state.pc - let elsePc = PC.add negatedCondition state.pc - if thenPc.IsFalse then - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - elif elsePc.IsFalse then - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - else - let checkBothBranches () = - conditionState.pc <- thenPc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - __insufficientInformation__ "Unable to witness branch" - | SolverInteraction.SmtSat elseModel -> - conditionState.pc <- elsePc - conditionState.model <- Some elseModel.mdl - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtUnsat _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - conditionState.pc <- elsePc - match SolverInteraction.checkSat conditionState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.model <- Some thenModel.mdl - conditionState.pc <- thenPc - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - conditionState.model <- Some thenModel.mdl - let thenState = conditionState - let elseState = copy conditionState |> withPc elsePc - elseState.model <- Some elseModel.mdl - thenState.pc <- thenPc - execution thenState elseState condition k - if tryEval && noNewConstants condition state.pc then - let evaluatedCondition = state.model.Value.Eval condition - // Current model satisfies new condition, so we can keep it for 'then' branch - if isTrue evaluatedCondition then - let elseState = copy conditionState |> withPc elsePc - conditionState.pc <- thenPc - match SolverInteraction.checkSat elseState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - thenBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat elseModel -> - elseState.pc <- elsePc - elseState.model <- Some elseModel.mdl - execution conditionState elseState condition k - // Current model satisfies !condition, so we can keep it for 'else' branch - elif isFalse evaluatedCondition then - let thenState = copy conditionState |> withPc thenPc - match SolverInteraction.checkSat thenState with - | SolverInteraction.SmtUnsat _ - | SolverInteraction.SmtUnknown _ -> - conditionState.pc <- elsePc - elseBranch conditionState (List.singleton >> k) - | SolverInteraction.SmtSat thenModel -> - let elseState = copy conditionState |> withPc elsePc - conditionState.pc <- thenPc - conditionState.model <- Some thenModel.mdl - execution conditionState elseState condition k - else - checkBothBranches() - else - checkBothBranches()) - - let branch copy (state : state) conditionInvocation thenBranch elseBranch merge2Results k = - if FeatureFlags.isConstraintIndependenceEnabled() then - executionWithConstraintIndependence true copy state conditionInvocation thenBranch elseBranch merge2Results k - else - executionWithoutConstraintIndependence true copy state conditionInvocation thenBranch elseBranch merge2Results k -*) - namespace VSharp.Core open VSharp diff --git a/VSharp.SILI.Core/Memory.fs b/VSharp.SILI.Core/Memory.fs index c8f37bd42..94c08bd06 100644 --- a/VSharp.SILI.Core/Memory.fs +++ b/VSharp.SILI.Core/Memory.fs @@ -275,7 +275,7 @@ module internal Memory = override x.IndependentWith otherSource = match otherSource with | :? structField as otherField -> - x.field <> otherField.field || x.baseSource.IndependentWith otherField.baseSource + x.field <> otherField.field && x.baseSource.IndependentWith otherField.baseSource | _ -> true let (|StructFieldSource|_|) (src : IMemoryAccessConstantSource) = diff --git a/VSharp.SILI.Core/PathCondition.fs b/VSharp.SILI.Core/PathCondition.fs index 616761b16..32cee9412 100644 --- a/VSharp.SILI.Core/PathCondition.fs +++ b/VSharp.SILI.Core/PathCondition.fs @@ -29,16 +29,17 @@ module internal PC = new() = PathCondition(HashSet(), false) override this.ToString() = - if (this :> IPathCondition).IsEmpty then "true" + if (this :> IPathCondition).IsEmpty then + if isFalse then "false" else "true" else Seq.map (fun c -> $"({c})") constraints |> join " /\ " interface IPathCondition with member this.Add newConstraint = match newConstraint with + | _ when isFalse -> () | True -> () | False -> becomeTrivialFalse() - | _ when isFalse -> () | _ when constraints.Contains(newConstraint) -> () // what if constraint is not equal to newConstraint structurally, but is equal logically? | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() @@ -211,7 +212,8 @@ module internal PC = internal new() = IndependentPathCondition(Dictionary(), HashSet(), false) override this.ToString() = - if (this :> IPathCondition).IsEmpty then "true" + if (this :> IPathCondition).IsEmpty then + if isFalse then "false" else "true" else Seq.map (fun c -> $"({c})") constraints |> join " /\ " interface IPathCondition with @@ -226,9 +228,9 @@ module internal PC = member this.Add newConstraint = match newConstraint with + | _ when isFalse -> () | True -> () | False -> becomeTrivialFalse() - | _ when isFalse -> () | _ when constraints.Contains(newConstraint) -> () // what if constraint is not equal to newConstraint structurally, but is equal logically? | _ when constraints.Contains(!!newConstraint) -> becomeTrivialFalse() diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 1eafb1582..087f43723 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -660,6 +660,7 @@ module internal Z3 = structureRef.Value <- x.WriteFields structureRef.Value value fields member x.UpdateModel (z3Model : Model) (targetModel : model) = + encodingCache.lastSymbolicAddress <- targetModel.state.startingTime.Head let stackEntries = Dictionary() encodingCache.termToExpression |> Seq.iter (fun kvp -> match kvp.Key with @@ -686,14 +687,14 @@ module internal Z3 = let term = x.Decode t refinedExpr targetModel.subst.[source] <- term | _ -> ()) + + if Memory.IsStackEmpty targetModel.state then + Memory.NewStackFrame targetModel.state None List.empty - let state = {Memory.EmptyState() with complete = true} - let frame = stackEntries |> Seq.map (fun kvp -> - let key = kvp.Key - let term = kvp.Value.Value - let typ = TypeOf term - (key, Some term, typ)) - Memory.NewStackFrame state None (List.ofSeq frame) + stackEntries |> Seq.iter (fun kvp -> + let key = kvp.Key + let term = kvp.Value.Value + Memory.AllocateOnStack targetModel.state key term) let defaultValues = Dictionary() encodingCache.regionConstants |> Seq.iter (fun kvp -> @@ -732,7 +733,7 @@ module internal Z3 = parseArray arr) defaultValues |> Seq.iter (fun kvp -> let region = kvp.Key - let constantValue = !kvp.Value + let constantValue = kvp.Value.Value Memory.FillRegion targetModel.state constantValue region) encodingCache.heapAddresses |> Seq.iter (fun kvp -> diff --git a/VSharp.Test/Tests/Lists.cs b/VSharp.Test/Tests/Lists.cs index 38ce534bd..543632eb2 100644 --- a/VSharp.Test/Tests/Lists.cs +++ b/VSharp.Test/Tests/Lists.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; using VSharp.Test; + #pragma warning disable CS0108, CS0114, CS0649 namespace IntegrationTests @@ -1337,7 +1338,7 @@ public bool ArrayContainsOurCustomer(Customerrr[] customers) } [TestSvm(100)] - public bool ContainsOurCustomer(Customerrr other) + public bool EqualsOurCustomer(Customerrr other) { if (other.Equals(_customer)) { diff --git a/VSharp.Utils/TaggedLogger.fs b/VSharp.Utils/TaggedLogger.fs index ea5b98785..9cf713dcd 100644 --- a/VSharp.Utils/TaggedLogger.fs +++ b/VSharp.Utils/TaggedLogger.fs @@ -26,7 +26,7 @@ module TaggedLogger = /// let public copy fromTag toTag = let from = logs.GetValueOrDefault(fromTag, StringBuilder()).ToString() - logs.[toTag] <- StringBuilder().AppendLine(from) + logs.[toTag] <- StringBuilder().Append(from) /// /// Saves all logs with the specified tag to the file From 814d398e936134259d10975207b0c68ad22586e4 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 3 Aug 2022 16:42:33 +0300 Subject: [PATCH 84/88] [fix] Fix Z3 builder cache clearing --- VSharp.API/Options.cs | 48 ++++----- VSharp.API/VSharp.cs | 2 +- VSharp.SILI.Core/SolverInteraction.fs | 1 + VSharp.Solver/Z3.fs | 148 +++++++++++++------------- VSharp.Test/IntegrationTests.cs | 2 +- 5 files changed, 99 insertions(+), 102 deletions(-) diff --git a/VSharp.API/Options.cs b/VSharp.API/Options.cs index d3afb41b7..6376e4795 100644 --- a/VSharp.API/Options.cs +++ b/VSharp.API/Options.cs @@ -4,11 +4,18 @@ namespace VSharp { /// - /// + /// Symbolic virtual machine execution mode. /// public enum ExecutionMode { + /// + /// Pure symbolic execution. Cannot cope with external dependencies in code. + /// Symbolic, + /// + /// (Experimental) Concrete and symbolic execution combination. Allows to explore code with external dependencies by + /// instantiating concrete objects. + /// Concolic } @@ -23,32 +30,24 @@ public enum SearchStrategy } /// - /// - /// - public enum CoverageZone - { - Method, - Class, - Module - } - - /// - /// + /// Parameters of the test generation run. /// - /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// Directory to place generated *.vst tests. If null or empty, process working directory is used. + /// null by default. /// - /// - /// + /// Symbolic virtual machine execution mode. by default. /// /// - /// Timeout for code exploration in seconds. Negative value means infinite timeout (up to exhaustive coverage or user interruption). - /// - /// - /// + /// Timeout for code exploration in seconds. Negative value means infinite timeout (up to exhaustive coverage or user interruption). + /// -1 by default + /// If true, constraint independence optimization is enabled: independent constraint sets are maintained + /// during symbolic execution. In general, improves execution time. true by default. + /// If true, SMT solver works in incremental mode during symbolic execution. May improve execution + /// time in some cases. false by default. + /// If true, symbolic execution process animation is saved in /visualize subdirectory of the . public readonly record struct CoverOptions( DirectoryInfo OutputDirectory = null, SearchStrategy SearchStrategy = SearchStrategy.DFS, - CoverageZone CoverageZone = CoverageZone.Method, ExecutionMode ExecutionMode = ExecutionMode.Symbolic, bool GuidedSearch = false, uint RecThreshold = 0u, @@ -58,15 +57,8 @@ public readonly record struct CoverOptions( bool Visualize = false ) { - internal SiliOptions ToSiliOptions() + internal SiliOptions ToSiliOptions(coverageZone coverageZone) { - var coverageZone = CoverageZone switch - { - CoverageZone.Method => Interpreter.IL.coverageZone.MethodZone, - CoverageZone.Class => Interpreter.IL.coverageZone.ClassZone, - CoverageZone.Module => Interpreter.IL.coverageZone.ModuleZone - }; - var searchMode = SearchStrategy switch { SearchStrategy.DFS => Interpreter.IL.searchMode.DFSMode, diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index 65691aa40..cb4a6ae8e 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -75,7 +75,7 @@ public static class TestGenerator { // TODO: customize search strategies via console options UnitTests unitTests = new UnitTests(options.OutputDirectory.FullName); - SILI explorer = new SILI(options.ToSiliOptions()); + SILI explorer = new SILI(options.ToSiliOptions(coverageZone.MethodZone)); Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled); diff --git a/VSharp.SILI.Core/SolverInteraction.fs b/VSharp.SILI.Core/SolverInteraction.fs index 3c4ba684c..087ec6365 100644 --- a/VSharp.SILI.Core/SolverInteraction.fs +++ b/VSharp.SILI.Core/SolverInteraction.fs @@ -2,6 +2,7 @@ namespace VSharp.Core open System.Collections.Generic open FSharpx.Collections +open Microsoft.FSharp.Core open VSharp module public SolverInteraction = diff --git a/VSharp.Solver/Z3.fs b/VSharp.Solver/Z3.fs index 087f43723..e9291fa24 100644 --- a/VSharp.Solver/Z3.fs +++ b/VSharp.Solver/Z3.fs @@ -75,7 +75,7 @@ module internal Z3 = let getMemoryConstant mkConst (typ : regionSort * fieldId list) = let result : ArrayExpr ref = ref null - if encodingCache.regionConstants.TryGetValue(typ, result) then !result + if encodingCache.regionConstants.TryGetValue(typ, result) then result.Value else let regConst = mkConst() encodingCache.regionConstants.Add(typ, regConst) @@ -84,8 +84,10 @@ module internal Z3 = member x.Reset() = encodingCache <- freshCache() - member x.ClearTermToExpressionCache() = + member x.ClearQueryScopedCache() = encodingCache.termToExpression.Clear() + encodingCache.heapAddresses.Clear() + encodingCache.regionConstants.Clear() member private x.ValidateId id = assert(not <| String.IsNullOrWhiteSpace id) @@ -743,9 +745,6 @@ module internal Z3 = targetModel.state.allocatedTypes <- PersistentDict.add addr typ targetModel.state.allocatedTypes) targetModel.state.startingTime <- VectorTime.min targetModel.state.startingTime [encodingCache.lastSymbolicAddress - 1] - encodingCache.heapAddresses.Clear() - encodingCache.termToExpression.Clear() - let private ctx = new Context() let private builder = Z3Builder(ctx) @@ -773,36 +772,38 @@ module internal Z3 = printLog Trace "SOLVER: trying to solve constraints..." printLogLazy Trace "%s" (lazy(q.ToString())) try - let query = builder.EncodeTerm encCtx q - let assumptions = query.assumptions - let assumptions = - seq { - yield! (Seq.cast<_> assumptions) - yield query.expr - } |> Array.ofSeq -// let pathAtoms = addSoftConstraints q.lvl - let result = optCtx.Check assumptions - match result with - | Status.SATISFIABLE -> - trace "SATISFIABLE" - let z3Model = optCtx.Model - let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} - builder.UpdateModel z3Model updatedModel - builder.ClearTermToExpressionCache() - SmtSat { mdl = updatedModel } - | Status.UNSATISFIABLE -> - trace "UNSATISFIABLE" - builder.ClearTermToExpressionCache() - SmtUnsat { core = Array.empty (*optCtx.UnsatCore |> Array.map (builder.Decode Bool)*) } - | Status.UNKNOWN -> - trace "UNKNOWN" - builder.ClearTermToExpressionCache() - SmtUnknown optCtx.ReasonUnknown - | _ -> __unreachable__() - with - | :? EncodingException as e -> - printLog Info "SOLVER: exception was thrown: %s" e.Message - SmtUnknown (sprintf "Z3 has thrown an exception: %s" e.Message) + try + let query = builder.EncodeTerm encCtx q + let assumptions = query.assumptions + let assumptions = + seq { + yield! (Seq.cast<_> assumptions) + yield query.expr + } + |> Seq.distinct |> Array.ofSeq +// let pathAtoms = addSoftConstraints q.lvl + let result = optCtx.Check assumptions + match result with + | Status.SATISFIABLE -> + trace "SATISFIABLE" + let z3Model = optCtx.Model + let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} + builder.UpdateModel z3Model updatedModel + SmtSat { mdl = updatedModel } + | Status.UNSATISFIABLE -> + trace "UNSATISFIABLE" + SmtUnsat { core = Array.empty (*optCtx.UnsatCore |> Array.map (builder.Decode Bool)*) } + | Status.UNKNOWN -> + trace "UNKNOWN" + SmtUnknown optCtx.ReasonUnknown + | _ -> __unreachable__() + with + | :? EncodingException as e -> + printLog Info "SOLVER: exception was thrown: %s" e.Message + SmtUnknown (sprintf "Z3 has thrown an exception: %s" e.Message) + finally + builder.ClearQueryScopedCache() + member x.Assert encCtx (fml : term) = printLogLazy Trace "SOLVER: Asserting: %s" (lazy(fml.ToString())) @@ -817,43 +818,46 @@ module internal Z3 = yield! Seq.cast encoded.assumptions yield encoded.expr :?> BoolExpr } - try - let exprs = Seq.collect encodeToBoolExprs formulas - let boolConsts = seq { - for expr in exprs do - let mutable name = "" - if assumptions.TryGetValue(expr, &name) then - yield ctx.MkBoolConst name - else - name <- $"p{assumptions.Count}" - printLog Trace $"SOLVER: assert: {name} => {expr}" - assumptions.[expr] <- name - let boolConst = ctx.MkBoolConst name - let implication = ctx.MkImplies(boolConst, expr) - optCtx.Assert implication - yield boolConst - } - let names = boolConsts |> Seq.map (fun c -> c.ToString()) - printLog Trace $"""SOLVER: check: {join " & " names}""" - let result = optCtx.Check boolConsts - match result with - | Status.SATISFIABLE -> - let z3Model = optCtx.Model - let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} - builder.UpdateModel z3Model updatedModel - builder.ClearTermToExpressionCache() - SmtSat { mdl = updatedModel } - | Status.UNSATISFIABLE -> - builder.ClearTermToExpressionCache() - SmtUnsat { core = Array.empty } - | Status.UNKNOWN -> - builder.ClearTermToExpressionCache() - SmtUnknown optCtx.ReasonUnknown - | _ -> __unreachable__() - with - | :? EncodingException as e -> - printLog Info "SOLVER: exception was thrown: %s" e.Message - SmtUnknown (sprintf "Z3 has thrown an exception: %s" e.Message) + try + try + let exprs = Seq.collect encodeToBoolExprs formulas |> Seq.distinct + let boolConsts = seq { + for expr in exprs do + let mutable name = "" + if assumptions.TryGetValue(expr, &name) then + yield ctx.MkBoolConst name + else + name <- $"p{assumptions.Count}" + printLog Trace $"SOLVER: assert: {name} => {expr}" + assumptions.[expr] <- name + let boolConst = ctx.MkBoolConst name + let implication = ctx.MkImplies(boolConst, expr) + optCtx.Assert implication + yield boolConst + } + let names = boolConsts |> Seq.map (fun c -> c.ToString()) + printLog Trace $"""SOLVER: check: {join " & " names}""" + let result = optCtx.Check boolConsts + match result with + | Status.SATISFIABLE -> + trace "SATISFIABLE" + let z3Model = optCtx.Model + let updatedModel = {currentModel with state = {currentModel.state with model = currentModel.state.model}} + builder.UpdateModel z3Model updatedModel + SmtSat { mdl = updatedModel } + | Status.UNSATISFIABLE -> + trace "UNSATISFIABLE" + SmtUnsat { core = Array.empty } + | Status.UNKNOWN -> + trace "UNKNOWN" + SmtUnknown optCtx.ReasonUnknown + | _ -> __unreachable__() + with + | :? EncodingException as e -> + printLog Info "SOLVER: exception was thrown: %s" e.Message + SmtUnknown (sprintf "Z3 has thrown an exception: %s" e.Message) + finally + builder.ClearQueryScopedCache() let reset() = builder.Reset() diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index e780ea52d..a16b1afc4 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -120,7 +120,7 @@ public TestSvmCommand( private TestResult Explore(TestExecutionContext context) { Core.API.ConfigureSolver(SolverPool.mkSolver(), false); - Core.API.SetConstraintIndependenceEnabled(true); + Core.API.SetConstraintIndependenceEnabled(false); var methodInfo = innerCommand.Test.Method.MethodInfo; try From c0f325f617614bdae4c6b55fe83614d48c6842d1 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 3 Aug 2022 16:45:00 +0300 Subject: [PATCH 85/88] [style] Remove Stopwatch and TaggedLogger from production --- VSharp.Utils/Stopwatch.fs | 123 ------------------------------- VSharp.Utils/TaggedLogger.fs | 39 ---------- VSharp.Utils/VSharp.Utils.fsproj | 2 - 3 files changed, 164 deletions(-) delete mode 100644 VSharp.Utils/Stopwatch.fs delete mode 100644 VSharp.Utils/TaggedLogger.fs diff --git a/VSharp.Utils/Stopwatch.fs b/VSharp.Utils/Stopwatch.fs deleted file mode 100644 index 92262793b..000000000 --- a/VSharp.Utils/Stopwatch.fs +++ /dev/null @@ -1,123 +0,0 @@ -namespace VSharp - -open System -open System.Collections.Generic -open System.Diagnostics -open System.Globalization -open System.IO -open CsvHelper -open CsvHelper.Configuration - -/// -/// Contains functions for running code with time measurement and measurement results export -/// -module Stopwatch = - - type private measurement = { stopwatch: Stopwatch; mutable timesCalled: int } - type private csvRecord = - { - commitHash: string - dateTime: string - caseName: string - tag: string - timesCalled: int - totalTicks: int64 - totalMs: int64 - testsGenerated: uint - } - - let private csvPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VSharpBenchmark") - let private csvFilename = "benchmark.csv" - let private measurements = Dictionary() - - let private getGitCommitHash() = - let procStartInfo = ProcessStartInfo("git", "rev-parse --short HEAD") - - procStartInfo.RedirectStandardOutput <- true - procStartInfo.UseShellExecute <- false - procStartInfo.CreateNoWindow <- true - - use proc = new Process() - proc.StartInfo <- procStartInfo - - proc.Start() |> ignore - proc.WaitForExit() - - proc.StandardOutput.ReadLine() - - /// - /// Runs function saving its execution time - /// - /// Tag to save results with. Results from multiple runs with the same tag are added up - /// Function to run - let public runMeasuringTime tag action = - let measurement = - if measurements.ContainsKey tag then - measurements.[tag] - else - let newMeasurement = { stopwatch = Stopwatch(); timesCalled = 0 } - measurements.[tag] <- newMeasurement - newMeasurement - if measurement.stopwatch.IsRunning then - measurement.timesCalled <- measurement.timesCalled + 1 - action() - else - try - measurement.stopwatch.Start() - measurement.timesCalled <- measurement.timesCalled + 1 - action() - finally - measurement.stopwatch.Stop() - - /// - /// Stops all running measurements - /// - let public stopAll() = - measurements - |> Seq.map (|KeyValue|) - |> Seq.iter (fun (_, m) -> m.stopwatch.Stop()) - - /// - /// Saves all current measurement results to .csv file. If the file already exists, appends lines - /// - /// Additional tag given to the saved measurements - /// Number of tests generated during the run - let public saveMeasurements caseName testsGenerated = - stopAll() - - let currentDateTime = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") - let commitHash = getGitCommitHash() - - let createRecord (tag, measurement : measurement) = - { - commitHash = commitHash - dateTime = currentDateTime - caseName = caseName - tag = tag - timesCalled = measurement.timesCalled - totalTicks = measurement.stopwatch.ElapsedTicks - totalMs = measurement.stopwatch.ElapsedMilliseconds - testsGenerated = testsGenerated - } - - let records = - measurements - |> Seq.map (|KeyValue|) - |> Seq.map createRecord - - let targetPath = Path.Combine(csvPath, csvFilename) - - let configuration = CsvConfiguration(CultureInfo.InvariantCulture) - configuration.IncludePrivateMembers <- true - configuration.HasHeaderRecord <- File.Exists(targetPath) |> not - - Directory.CreateDirectory(csvPath) |> ignore - use streamWriter = File.AppendText(targetPath) - use csvWriter = new CsvWriter(streamWriter, configuration) - - csvWriter.WriteRecords(records) - - /// - /// Clears all current measurements - /// - let public clear() = measurements.Clear() diff --git a/VSharp.Utils/TaggedLogger.fs b/VSharp.Utils/TaggedLogger.fs deleted file mode 100644 index 9cf713dcd..000000000 --- a/VSharp.Utils/TaggedLogger.fs +++ /dev/null @@ -1,39 +0,0 @@ -namespace VSharp - -open System.Collections.Generic -open System.IO -open System.Text - -/// -/// Contains functions for saving logs with tag keys. -/// May be used to save logs from different states separately -/// -module TaggedLogger = - - let logs = Dictionary() - - /// - /// Saves log with the tag - /// - /// Tag to save the log with - /// Message to log - let public log tag (message : string) = - if not <| logs.ContainsKey(tag) then logs.Add(tag, StringBuilder()) - logs.[tag].AppendLine(message) |> ignore - - /// - /// Saves with toTag all logs with fromTag - /// - let public copy fromTag toTag = - let from = logs.GetValueOrDefault(fromTag, StringBuilder()).ToString() - logs.[toTag] <- StringBuilder().Append(from) - - /// - /// Saves all logs with the specified tag to the file - /// - /// Tag to save the logs with - /// Path of the file to save - let public saveLog tag path = - if logs.ContainsKey tag then - let log = logs.[tag].ToString() - File.WriteAllText(path, log) diff --git a/VSharp.Utils/VSharp.Utils.fsproj b/VSharp.Utils/VSharp.Utils.fsproj index a3c3529b0..bedbde253 100644 --- a/VSharp.Utils/VSharp.Utils.fsproj +++ b/VSharp.Utils/VSharp.Utils.fsproj @@ -17,9 +17,7 @@ - - From b5a2ebd47cb65779496d5d625bee6ff2e01edb60 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 3 Aug 2022 16:46:06 +0300 Subject: [PATCH 86/88] [fix] Enable constraint-independence for integration tests --- VSharp.Test/IntegrationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSharp.Test/IntegrationTests.cs b/VSharp.Test/IntegrationTests.cs index a16b1afc4..e780ea52d 100644 --- a/VSharp.Test/IntegrationTests.cs +++ b/VSharp.Test/IntegrationTests.cs @@ -120,7 +120,7 @@ public TestSvmCommand( private TestResult Explore(TestExecutionContext context) { Core.API.ConfigureSolver(SolverPool.mkSolver(), false); - Core.API.SetConstraintIndependenceEnabled(false); + Core.API.SetConstraintIndependenceEnabled(true); var methodInfo = innerCommand.Test.Method.MethodInfo; try From 53419b6867bcc05605969283e14ec694c5d1453c Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 3 Aug 2022 17:36:19 +0300 Subject: [PATCH 87/88] [fix] In incremental mode, create solver for each covered method --- VSharp.API/VSharp.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index cb4a6ae8e..f124c745d 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -28,7 +28,7 @@ internal Statistics(TimeSpan time, DirectoryInfo outputDir, uint tests, uint err public TimeSpan TestGenerationTime { get; } /// - /// Directory where *.vst tests are placed + /// Directory where *.vst tests are placed. /// public DirectoryInfo OutputDir { get; } @@ -76,12 +76,17 @@ public static class TestGenerator // TODO: customize search strategies via console options UnitTests unitTests = new UnitTests(options.OutputDirectory.FullName); SILI explorer = new SILI(options.ToSiliOptions(coverageZone.MethodZone)); - Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled); - - // TODO: single solver instance for multiple methods causes problems in incrementality mode + + var i = 0; foreach (var method in methods) { + // When solver works in incremental mode, it should be recreated for each scope to clear cached stuff + if (i == 0 || options.IsSolverIncrementalityEnabled) + { + Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); + } + if (method == method.Module.Assembly.EntryPoint) { explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, @@ -92,6 +97,8 @@ public static class TestGenerator explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); } + + ++i; } var statistics = new Statistics(explorer.Statistics.CurrentExplorationTime, unitTests.TestDirectory, From 0ffc829cc429420b499692c6e15e9eedb8ea1679 Mon Sep 17 00:00:00 2001 From: Maksim Parshin Date: Wed, 3 Aug 2022 19:15:46 +0300 Subject: [PATCH 88/88] [fix] CLI options style fixes + remove new solver creation for each method --- VSharp.API/VSharp.cs | 14 ++++------- VSharp.Runner/RunnerProgram.cs | 44 ++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/VSharp.API/VSharp.cs b/VSharp.API/VSharp.cs index f124c745d..aaa9ac348 100644 --- a/VSharp.API/VSharp.cs +++ b/VSharp.API/VSharp.cs @@ -73,20 +73,16 @@ public static class TestGenerator { private static Statistics StartExploration(List methods, CoverOptions options = new(), string[] mainArguments = null) { + var outputDirectory = options.OutputDirectory ?? new DirectoryInfo(Directory.GetCurrentDirectory()); // TODO: customize search strategies via console options - UnitTests unitTests = new UnitTests(options.OutputDirectory.FullName); + UnitTests unitTests = new UnitTests(outputDirectory.FullName); SILI explorer = new SILI(options.ToSiliOptions(coverageZone.MethodZone)); + // TODO: refresh solver ctx for each method in incremental mode + Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); Core.API.SetConstraintIndependenceEnabled(options.IsConstraintIndependenceEnabled); - var i = 0; foreach (var method in methods) { - // When solver works in incremental mode, it should be recreated for each scope to clear cached stuff - if (i == 0 || options.IsSolverIncrementalityEnabled) - { - Core.API.ConfigureSolver(SolverPool.mkSolver(), options.IsSolverIncrementalityEnabled); - } - if (method == method.Module.Assembly.EntryPoint) { explorer.InterpretEntryPoint(method, mainArguments, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, @@ -97,8 +93,6 @@ public static class TestGenerator explorer.InterpretIsolated(method, unitTests.GenerateTest, unitTests.GenerateError, _ => { }, e => throw e); } - - ++i; } var statistics = new Statistics(explorer.Statistics.CurrentExplorationTime, unitTests.TestDirectory, diff --git a/VSharp.Runner/RunnerProgram.cs b/VSharp.Runner/RunnerProgram.cs index 5a0bd46ed..323d88788 100644 --- a/VSharp.Runner/RunnerProgram.cs +++ b/VSharp.Runner/RunnerProgram.cs @@ -110,30 +110,44 @@ private static void PostProcess(Statistics statistics) public static int Main(string[] args) { + var defaultOptions = new CoverOptions(); + var assemblyPathArgument = new Argument("assembly-path", description: "Path to the target assembly"); - var timeoutOption = - new Option(aliases: new[] { "--timeout", "-t" }, - () => -1, - "Time for test generation. Negative values mean no timeout"); - var outputOption = - new Option(aliases: new[] { "--output", "-o" }, - () => new DirectoryInfo(Directory.GetCurrentDirectory()), - "Path where unit tests will be generated"); + + var timeoutOption = new Option( + aliases: new[] { "--timeout", "-t" }, + description: "Time for test generation. Negative values mean no timeout", + getDefaultValue: () => defaultOptions.Timeout + ); + + var outputOption = new Option( + aliases: new[] { "--output", "-o" }, + description: "Path where unit tests will be generated. Process working directory by default", + getDefaultValue: () => defaultOptions.OutputDirectory + ); + var concreteArguments = new Argument("args", description: "Command line arguments"); - var unknownArgsOption = - new Option("--unknown-args", description: "Force engine to generate various input console arguments"); - + + var unknownArgsOption = new Option( + "--unknown-args", + description: "Force engine to generate various input console arguments", + getDefaultValue: () => false + ); + var constraintIndependenceOption = new Option( "--c-independence", - description: "Maintain independent constraint sets (constraint independence optimization). True by default", - getDefaultValue: () => true + description: "If true, constraint independence optimization is enabled: independent constraint sets are maintained " + + "during symbolic execution. In general, improves execution time", + getDefaultValue: () => defaultOptions.IsConstraintIndependenceEnabled ); + var incrementalityOption = new Option( "--incrementality", - description: "Enable SMT solver incremental mode. False by default", - getDefaultValue: () => false + description: "If true, SMT solver works in incremental mode during symbolic execution. May improve execution time" + + " in some cases", + getDefaultValue: () => defaultOptions.IsSolverIncrementalityEnabled ); var rootCommand = new RootCommand();