Skip to content

Commit

Permalink
Merge branch 'master' into release/Byakko
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveGilham committed Mar 30, 2018
2 parents cf94a72 + 0c4a6f5 commit bc2b7d1
Show file tree
Hide file tree
Showing 17 changed files with 661 additions and 204 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -11,6 +11,7 @@ if: NOT branch =~ ^develop/.*$ OR branch =~ ^develop/travis/.*$
cache:
directories:
- packages
- Demo/Service/packages
- $HOME/.nuget

script:
Expand Down
42 changes: 31 additions & 11 deletions AltCover.Recorder/Base.fs
Expand Up @@ -20,6 +20,12 @@ type internal Track =
| Both of (int64 * int)

module Counter =
/// <summary>
/// The offset flag for branch counts
/// </summary>
let internal BranchFlag = 0x80000000
let internal BranchMask = 0x7FFFFFFF

/// <summary>
/// The time at which coverage run began
/// </summary>
Expand Down Expand Up @@ -52,11 +58,23 @@ module Counter =
let private WriteXDocument (coverageDocument:XmlDocument) (stream:Stream) =
coverageDocument.Save(stream)

let internal FindIndexFromUspid uspid =
let internal FindIndexFromUspid flag uspid =
let f, c = Int32.TryParse( uspid ,
System.Globalization.NumberStyles.Integer,
System.Globalization.CultureInfo.InvariantCulture)
if f then c else -1
if f then (c|||flag) else -1

let internal OpenCoverXml = ("//Module", "hash", "Classes/Class/Methods/Method",
[("SequencePoints/SequencePoint", 0)
("BranchPoints/BranchPoint", BranchFlag)]
, "vc")
let internal NCoverXml = ("//module", "moduleId", "method", [("seqpnt",0)], "visitcount")

let internal XmlByFormat format =
match format with
| ReportFormat.OpenCoverWithTracking
| ReportFormat.OpenCover -> OpenCoverXml
| _ -> NCoverXml

/// <summary>
/// Save sequence point hit counts to xml report file
Expand Down Expand Up @@ -85,10 +103,7 @@ module Counter =
root.SetAttribute("driverVersion", "AltCover.Recorder " +
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion)

let (m, i, m', s, v) = match format with
| ReportFormat.OpenCoverWithTracking
| ReportFormat.OpenCover -> ("//Module", "hash", "Classes/Class/Methods/Method", "SequencePoints/SequencePoint", "vc")
| _ -> ("//module", "moduleId", "method", "seqpnt", "visitcount")
let (m, i, m', s, v) = XmlByFormat format
coverageDocument.SelectNodes(m)
|> Seq.cast<XmlElement>
|> Seq.map (fun el -> el.GetAttribute(i), el)
Expand All @@ -103,12 +118,17 @@ module Counter =
let nn = affectedModule.SelectNodes(m')
nn
|> Seq.cast<XmlElement>
|> Seq.collect (fun (``method``:XmlElement) -> ``method``.SelectNodes(s)
|> Seq.cast<XmlElement>
|> Seq.toList |> List.rev)
|> Seq.mapi (fun counter pt -> ((match format with
|> Seq.collect (fun (``method``:XmlElement) ->
s
|> Seq.collect (fun (name, flag) ->
``method``.SelectNodes(name)
|> Seq.cast<XmlElement>
|> Seq.map (fun x -> (x,flag))
|> Seq.toList |> List.rev))
|> Seq.mapi (fun counter (pt,flag) ->
((match format with
| ReportFormat.OpenCoverWithTracking
| ReportFormat.OpenCover -> "uspid" |> pt.GetAttribute |> FindIndexFromUspid
| ReportFormat.OpenCover -> "uspid" |> pt.GetAttribute |> (FindIndexFromUspid flag)
| _ -> counter),
pt))
|> Seq.filter (fst >> moduleHits.ContainsKey)
Expand Down
124 changes: 81 additions & 43 deletions AltCover/Instrument.fs
Expand Up @@ -305,7 +305,7 @@ module Instrument =
|> Seq.tryFindIndex (fun x -> x = oldValue)
match offset with
| Some i -> // operands.[i] <- newValue : fails with "This expression was expected to have type ''a [] * int * 'a' but here has type 'Instruction array'"
Array.blit [| newValue |] 0 operands i 1 // so mutate the array like this instead
Array.set operands i newValue // so mutate the array like this instead
| _ -> ()
| _ -> ()

Expand Down Expand Up @@ -392,18 +392,79 @@ module Instrument =
MethodWorker = body.GetILProcessor() }
| _ -> state

let private VisitMethodPoint (state : Context) instruction point included =
if included then // by construction the sequence point is included
let instrLoadModuleId = InsertVisit instruction state.MethodWorker state.RecordingMethodRef.Visit state.ModuleId point
let private UpdateBranchReferences state instruction injected =
// Change references in operands from "instruction" to first counter invocation instruction (instrLoadModuleId)
let subs = SubstituteInstruction (instruction, injected)
state.MethodBody.Instructions
|> Seq.iter subs.SubstituteInstructionOperand

state.MethodBody.ExceptionHandlers
|> Seq.iter subs.SubstituteExceptionBoundary

// Change references in operands from "instruction" to first counter invocation instruction (instrLoadModuleId)
let subs = SubstituteInstruction (instruction, instrLoadModuleId)
state.MethodBody.Instructions
|> Seq.iter subs.SubstituteInstructionOperand
let private VisitMethodPoint (state : Context) instruction point included =
if included then // by construction the sequence point is included
let instrLoadModuleId = InsertVisit instruction state.MethodWorker state.RecordingMethodRef.Visit state.ModuleId point
UpdateBranchReferences state instruction instrLoadModuleId
state

state.MethodBody.ExceptionHandlers
|> Seq.iter subs.SubstituteExceptionBoundary
state
let internal VisitBranchPoint (state:Context) branch =
if state.MethodWorker |> isNull |> not then
let point = (branch.Uid ||| Base.Counter.BranchFlag)
let instrument instruction =
InsertVisit instruction state.MethodWorker state.RecordingMethodRef.Visit state.ModuleId point

let updateSwitch update =
let operands = branch.Start.Operand :?> Instruction[]
branch.Indexes
|> Seq.filter (fun i -> i >= 0)
// See SubstituteInstructionOperand for why we do it this way
|> Seq.iter (fun i -> Array.set operands i update)

match branch.Indexes |> Seq.tryFind (fun i -> i = -1) with
| Some _ -> // immediate next instruction; by construction this one comes first
// before
// Cond_Branch xxx
// Next
//
// after
// Cond_Branch xxx
// jump instrument#-1
// instrument#-1
// Next
let target = branch.Start.Next
let preamble = instrument target
let jump = state.MethodWorker.Create(OpCodes.Br, preamble)
state.MethodWorker.InsertAfter (branch.Start, jump)
if branch.Start.OpCode = OpCodes.Switch then
updateSwitch jump
| None ->
// before
// Cond_Branch #n
// jump instrument#-1
// ...
// instrument#-1
// Next
//
// after
// Cond_Branch instrument#n
// jump instrument#-1
// instrument#n
// jump #n
// ...
// instrument#-1
// Next
let target = if branch.Start.OpCode = OpCodes.Switch then
branch.Start.Operand :?> Instruction[]
|> Seq.skip (branch.Indexes.Head)
|> Seq.head
else branch.Start.Operand :?> Instruction
let jump = state.MethodWorker.Create(OpCodes.Br, target)
state.MethodWorker.InsertAfter (branch.Start.Next, jump)
let preamble = instrument jump
if branch.Start.OpCode = OpCodes.Switch then
updateSwitch preamble
else branch.Start.Operand <- preamble
state

let private FinishVisit (state : Context) =
let counterAssemblyFile = Path.Combine(Visitor.OutputDirectory(), (extractName state.RecordingAssembly) + ".dll")
Expand Down Expand Up @@ -456,34 +517,7 @@ module Instrument =
methodWorker.InsertBefore (handler.TryStart, pushMethodCall)
methodWorker.InsertBefore(pushMethodCall, instrLoadId)
)
(*
// Instance.Push(666);
IL_0000: ldc.i4 666
IL_0005: call void [AltCover.Recorder]AltCover.Recorder.Instance::Push(int32)
.try
{
// (no C# code)
IL_000a: nop
...
IL_0025: stloc.0
// (no C# code)
IL_0026: leave.s IL_0031
} // end .try
finally
{
IL_0028: nop
// Instance.Pop();
IL_0029: call void [AltCover.Recorder]AltCover.Recorder.Instance::Pop()
// (no C# code)
IL_002e: ldnull
IL_002f: pop
IL_0030: endfinally
} // end handler
IL_0031: ldloc.0
ahead of
IL_0032: ret
*)

let private VisitAfterMethod state m included track =
if Visitor.IsInstrumented included then
let body = state.MethodBody
Expand All @@ -494,6 +528,12 @@ IL_0032: ret
Track state m included track
state

let private VisitAfterAssembly state (assembly:AssemblyDefinition) =
let originalFileName = Path.GetFileName assembly.MainModule.FileName
let path = Path.Combine(Visitor.OutputDirectory(), originalFileName)
WriteAssembly assembly path
state

/// <summary>
/// Perform visitor operations
/// </summary>
Expand All @@ -513,14 +553,12 @@ IL_0032: ret

| MethodPoint (instruction, _, point, included) ->
VisitMethodPoint state instruction point included
| BranchPoint branch -> VisitBranchPoint state branch
| AfterMethod (m, included, track) -> VisitAfterMethod state m included track

| AfterType -> state
| AfterModule -> state
| AfterAssembly assembly -> let originalFileName = Path.GetFileName assembly.MainModule.FileName
let path = Path.Combine(Visitor.OutputDirectory(), originalFileName)
WriteAssembly assembly path
state
| AfterAssembly assembly -> VisitAfterAssembly state assembly
| Finish -> FinishVisit state

/// <summary>
Expand Down

0 comments on commit bc2b7d1

Please sign in to comment.