private static XElement CreateMethodElement(InstrumentedMethod method, IEnumerable <InstrumentedSequence> instructions, HitsInfo hitsInfo) { var allLines = instructions .SelectMany(i => i.GetLines()) .Distinct() .Count(); var coveredLines = instructions .Where(h => hitsInfo.WasHit(h.HitId)) .SelectMany(i => i.GetLines()) .Distinct() .Count(); var allBranches = instructions .SelectMany(i => i.Conditions) .SelectMany(c => c.Branches) .Count(); var coveredBranches = instructions .SelectMany(i => i.Conditions) .SelectMany(c => c.Branches) .Where(b => hitsInfo.WasHit(b.HitId)) .Count(); var lineRate = allLines == 0 ? 1d : (double)coveredLines / (double)allLines; var branchRate = allBranches == 0 ? 1d : (double)coveredBranches / (double)allBranches; var openParametersIndex = method.FullName.IndexOf("("); var signature = method.FullName.Substring(openParametersIndex); return(new XElement( XName.Get("method"), new XAttribute(XName.Get("name"), method.Name), new XAttribute(XName.Get("signature"), signature), new XAttribute(XName.Get("line-rate"), lineRate), new XAttribute(XName.Get("branch-rate"), branchRate), new XAttribute(XName.Get("complexity"), 0), CreateLinesElement(instructions, hitsInfo) )); }
private void InstrumentInstructions( InstrumentationContext context, MethodDefinition methodDefinition, InstrumentedAssembly instrumentedAssembly, Dictionary <int, Instruction> instructionsByOffset, ILProcessor ilProcessor, VariableDefinition methodContextVariable, InstrumentedMethod instrumentedMethod) { var hitInstructionReference = methodDefinition.Module.GetOrImportReference(hitInstructionMethodInfo); foreach (var sequencePoint in methodDefinition.DebugInformation.SequencePoints) { var document = sequencePoint.Document; if (document.FileHasChanged()) { _logger.LogInformation("Ignoring modified file {file}", document.Url); continue; } if (sequencePoint.IsHidden) { continue; } var documentUrl = sequencePoint.Document.Url; var documentLines = _fileReader.ReadAllLines(new FileInfo(documentUrl)); var code = documentLines.ExtractCode( sequencePoint.StartLine, sequencePoint.EndLine, sequencePoint.StartColumn, sequencePoint.EndColumn); if (code == null || code == "{" || code == "}") { continue; } var instruction = instructionsByOffset[sequencePoint.Offset]; // if the previous instruction is a Prefix instruction then this instruction MUST go with it. // we cannot put an instruction between the two. if (instruction.Previous != null && instruction.Previous.OpCode.OpCodeType == OpCodeType.Prefix) { continue; } if (!ilProcessor.Body.Instructions.Contains(instruction)) { var methodFullName = $"{methodDefinition.DeclaringType.FullName}.{methodDefinition.Name}"; _logger.LogWarning("Skipping instruction because it was removed from method {method}", methodFullName); continue; } var sourceRelativePath = GetSourceRelativePath(context, documentUrl); var instructionId = ++context.InstructionId; instrumentedAssembly.AddInstruction(sourceRelativePath, new InstrumentedInstruction { Id = instructionId, StartLine = sequencePoint.StartLine, EndLine = sequencePoint.EndLine, StartColumn = sequencePoint.StartColumn, EndColumn = sequencePoint.EndColumn, Instruction = instruction.ToString(), Method = instrumentedMethod, Code = code }); ilProcessor.InsertBefore(instruction, new[] { ilProcessor.Create(OpCodes.Ldloc, methodContextVariable), ilProcessor.Create(OpCodes.Ldc_I4, instructionId), ilProcessor.Create(OpCodes.Callvirt, hitInstructionReference) }, true); } }